DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v9 04/21] net/ngbe: fix VF promiscuous and allmulticast
From: Zaiyu Wang @ 2026-06-22 11:10 UTC (permalink / raw)
  To: dev; +Cc: Zaiyu Wang, stable, Jiawen Wu
In-Reply-To: <20260622111111.21024-1-zaiyuwang@trustnetic.com>

The configuration of allmulti and promiscuous modes conflicts
together. For instance, if we enable promiscuous mode, then enable and
disable allmulti, then the promiscuous mode is wrongly disabled.

To resolve this, the following changes are made:
- do nothing when we set/unset allmulti if promiscuous mode is on
- restore the proper mode (none or allmulti) when we disable
  promiscuous mode

Fixes: 7744e90805b5 ("net/ngbe: add promiscuous and allmulticast ops for VF device")
Cc: stable@dpdk.org

Signed-off-by: Zaiyu Wang <zaiyuwang@trustnetic.com>
---
 drivers/net/ngbe/ngbe_ethdev_vf.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ngbe/ngbe_ethdev_vf.c b/drivers/net/ngbe/ngbe_ethdev_vf.c
index ea3a988df6..c440e02369 100644
--- a/drivers/net/ngbe/ngbe_ethdev_vf.c
+++ b/drivers/net/ngbe/ngbe_ethdev_vf.c
@@ -1196,9 +1196,13 @@ static int
 ngbevf_dev_promiscuous_disable(struct rte_eth_dev *dev)
 {
 	struct ngbe_hw *hw = ngbe_dev_hw(dev);
+	int mode = NGBEVF_XCAST_MODE_NONE;
 	int ret;
 
-	switch (hw->mac.update_xcast_mode(hw, NGBEVF_XCAST_MODE_NONE)) {
+	if (dev->data->all_multicast)
+		mode = NGBEVF_XCAST_MODE_ALLMULTI;
+
+	switch (hw->mac.update_xcast_mode(hw, mode)) {
 	case 0:
 		ret = 0;
 		break;
@@ -1219,7 +1223,7 @@ ngbevf_dev_allmulticast_enable(struct rte_eth_dev *dev)
 	struct ngbe_hw *hw = ngbe_dev_hw(dev);
 	int ret;
 
-	if (dev->data->promiscuous == 1)
+	if (dev->data->promiscuous)
 		return 0;
 
 	switch (hw->mac.update_xcast_mode(hw, NGBEVF_XCAST_MODE_ALLMULTI)) {
@@ -1243,6 +1247,9 @@ ngbevf_dev_allmulticast_disable(struct rte_eth_dev *dev)
 	struct ngbe_hw *hw = ngbe_dev_hw(dev);
 	int ret;
 
+	if (dev->data->promiscuous)
+		return 0;
+
 	switch (hw->mac.update_xcast_mode(hw, NGBEVF_XCAST_MODE_MULTI)) {
 	case 0:
 		ret = 0;
-- 
2.21.0.windows.1


^ permalink raw reply related

* [PATCH v9 03/21] net/ngbe: add missing CDR config for YT PHY
From: Zaiyu Wang @ 2026-06-22 11:10 UTC (permalink / raw)
  To: dev; +Cc: Zaiyu Wang, stable, Jiawen Wu
In-Reply-To: <20260622111111.21024-1-zaiyuwang@trustnetic.com>

According to the PHY vendor, when YT8531S operates in UTP-to-Fiber or
RGMII-to-Fiber mode with auto-negotiation disabled (Force mode),
additional CDR (Clock Data Recovery) configuration is required to
improve link connectivity. Without this config, link may be unstable
or fail to establish.

Fixes: f1268369403d ("net/ngbe: support autoneg on/off for external PHY SFI mode")
Cc: stable@dpdk.org

Signed-off-by: Zaiyu Wang <zaiyuwang@trustnetic.com>
---
 drivers/net/ngbe/base/ngbe_phy_yt.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/ngbe/base/ngbe_phy_yt.c b/drivers/net/ngbe/base/ngbe_phy_yt.c
index d110fbc8b2..ab0778d246 100644
--- a/drivers/net/ngbe/base/ngbe_phy_yt.c
+++ b/drivers/net/ngbe/base/ngbe_phy_yt.c
@@ -264,6 +264,9 @@ s32 ngbe_setup_phy_link_yt(struct ngbe_hw *hw, u32 speed,
 			value = YT_BCR_RESET | YT_BCR_ANE | YT_BCR_RESTART_AN |
 				YT_BCR_DUPLEX | YT_BCR_SPEED_SELECT1;
 		} else {
+			/* force mode need to config cdr */
+			ngbe_write_phy_reg_sds_ext_yt(hw, 0x3, 0, 0x1434);
+			ngbe_write_phy_reg_sds_ext_yt(hw, 0xe, 0, 0x163);
 			value = YT_BCR_RESET | YT_BCR_DUPLEX;
 			if (speed & NGBE_LINK_SPEED_1GB_FULL)
 				value |= YT_BCR_SPEED_SELECT1;
-- 
2.21.0.windows.1


^ permalink raw reply related

* [PATCH v9 01/21] net/txgbe: remove duplicate xstats counters
From: Zaiyu Wang @ 2026-06-22 11:10 UTC (permalink / raw)
  To: dev; +Cc: Zaiyu Wang, stable, Jiawen Wu, Ferruh Yigit
In-Reply-To: <20260622111111.21024-1-zaiyuwang@trustnetic.com>

Remove four redundant counters (tx_xon_packets, rx_xon_packets,
tx_xoff_packets and rx_xoff_packets) from xstats, as they were duplicates
of tx_flow_control_xon_packets and others. Both sets were reading the same
registers but being output twice under different names. After removing
these entries, the flow control counters in DPDK now align with those in
our Linux kernel driver.

Fixes: 91fe49c87d76 ("net/txgbe: support device xstats")
Cc: stable@dpdk.org

Signed-off-by: Zaiyu Wang <zaiyuwang@trustnetic.com>
---
 drivers/net/txgbe/txgbe_ethdev.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/drivers/net/txgbe/txgbe_ethdev.c b/drivers/net/txgbe/txgbe_ethdev.c
index 0f484dfe91..77870f3aee 100644
--- a/drivers/net/txgbe/txgbe_ethdev.c
+++ b/drivers/net/txgbe/txgbe_ethdev.c
@@ -261,11 +261,6 @@ static const struct rte_txgbe_xstats_name_off rte_txgbe_stats_strings[] = {
 	HW_XSTAT(tx_size_1024_to_max_packets),
 
 	/* Flow Control */
-	HW_XSTAT(tx_xon_packets),
-	HW_XSTAT(rx_xon_packets),
-	HW_XSTAT(tx_xoff_packets),
-	HW_XSTAT(rx_xoff_packets),
-
 	HW_XSTAT_NAME(tx_xon_packets, "tx_flow_control_xon_packets"),
 	HW_XSTAT_NAME(rx_xon_packets, "rx_flow_control_xon_packets"),
 	HW_XSTAT_NAME(tx_xoff_packets, "tx_flow_control_xoff_packets"),
-- 
2.21.0.windows.1


^ permalink raw reply related

* [PATCH v9 02/21] net/ngbe: remove duplicate xstats counters
From: Zaiyu Wang @ 2026-06-22 11:10 UTC (permalink / raw)
  To: dev; +Cc: Zaiyu Wang, stable, Jiawen Wu
In-Reply-To: <20260622111111.21024-1-zaiyuwang@trustnetic.com>

Remove four redundant counters (tx_xon_packets, rx_xon_packets,
tx_xoff_packets and rx_xoff_packets) from xstats, as they were duplicates
of tx_flow_control_xon_packets and others. Both sets were reading the same
registers but being output twice under different names. After removing
these entries, the flow control counters in DPDK now align with those in
our Linux kernel driver.

Fixes: 8b433d04adc9 ("net/ngbe: support device xstats")
Cc: stable@dpdk.org

Signed-off-by: Zaiyu Wang <zaiyuwang@trustnetic.com>
---
 drivers/net/ngbe/ngbe_ethdev.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/drivers/net/ngbe/ngbe_ethdev.c b/drivers/net/ngbe/ngbe_ethdev.c
index f7b4a8b159..09bcd791ed 100644
--- a/drivers/net/ngbe/ngbe_ethdev.c
+++ b/drivers/net/ngbe/ngbe_ethdev.c
@@ -227,11 +227,6 @@ static const struct rte_ngbe_xstats_name_off rte_ngbe_stats_strings[] = {
 	HW_XSTAT(tx_size_1024_to_max_packets),
 
 	/* Flow Control */
-	HW_XSTAT(tx_xon_packets),
-	HW_XSTAT(rx_xon_packets),
-	HW_XSTAT(tx_xoff_packets),
-	HW_XSTAT(rx_xoff_packets),
-
 	HW_XSTAT_NAME(tx_xon_packets, "tx_flow_control_xon_packets"),
 	HW_XSTAT_NAME(rx_xon_packets, "rx_flow_control_xon_packets"),
 	HW_XSTAT_NAME(tx_xoff_packets, "tx_flow_control_xoff_packets"),
-- 
2.21.0.windows.1


^ permalink raw reply related

* [PATCH v9 00/21] Wangxun Fixes
From: Zaiyu Wang @ 2026-06-22 11:10 UTC (permalink / raw)
  To: dev; +Cc: Zaiyu Wang
In-Reply-To: <20260423034024.14404-1-zaiyuwang@trustnetic.com>

This series fixes several issues found on Wangxun Emerald, Sapphire and
Amber-lite NICs, with a focus on link-related problems.
---
v9:
- Fixed several checkpatch errors
---
v8:
- Fixed compilation error by replacing RTE_ETH_DEV_TO_PCI with RTE_CLASS_TO_BUS_DEVICE
---
v7:
- Fixed inverted semantics of is_flat_mem to match SFF8636
---
v6:
- Fixed more issues identified by AI review
---
v5:
- Fixed issues identified by AI review
---
v4:
- Fixed issues identified by devtools scripts
---
v3:
- Addressed Stephen's comments
---
v2:
- Fixed compilation error and code style issues
---

Zaiyu Wang (21):
  net/txgbe: remove duplicate xstats counters
  net/ngbe: remove duplicate xstats counters
  net/ngbe: add missing CDR config for YT PHY
  net/ngbe: fix VF promiscuous and allmulticast
  net/txgbe: fix inaccuracy in Tx rate limiting
  net/txgbe: fix link status check condition
  net/txgbe: fix Tx desc free logic
  net/txgbe: fix link flow control registers for Amber-Lite
  net/txgbe: fix link flow control config for Sapphire
  net/txgbe: fix a mass of unknown interrupts
  net/txgbe: fix traffic class priority configuration
  net/txgbe: fix link stability for 25G NIC
  net/txgbe: fix link stability for 40G NIC
  net/txgbe: fix link stability for Amber-Lite backplane mode
  net/txgbe: fix FEC mode configuration on 25G NIC
  net/txgbe: fix SFP module identification
  net/txgbe: fix get module info operation
  net/txgbe: fix get EEPROM operation
  net/txgbe: fix to reset Tx write-back pointer
  net/txgbe: fix to enable Tx desc check
  net/txgbe: fix temperature track for AML NIC

 drivers/net/ngbe/base/ngbe_phy_yt.c       |    3 +
 drivers/net/ngbe/ngbe_ethdev.c            |    5 -
 drivers/net/ngbe/ngbe_ethdev_vf.c         |   11 +-
 drivers/net/txgbe/base/meson.build        |    2 +
 drivers/net/txgbe/base/txgbe.h            |    2 +
 drivers/net/txgbe/base/txgbe_aml.c        |  185 +-
 drivers/net/txgbe/base/txgbe_aml.h        |    6 +-
 drivers/net/txgbe/base/txgbe_aml40.c      |  114 +-
 drivers/net/txgbe/base/txgbe_aml40.h      |    6 +-
 drivers/net/txgbe/base/txgbe_dcb_hw.c     |    2 +-
 drivers/net/txgbe/base/txgbe_e56.c        | 3773 +++++++++++++++++++++
 drivers/net/txgbe/base/txgbe_e56.h        | 1753 ++++++++++
 drivers/net/txgbe/base/txgbe_e56_bp.c     | 2597 ++++++++++++++
 drivers/net/txgbe/base/txgbe_e56_bp.h     |  282 ++
 drivers/net/txgbe/base/txgbe_hw.c         |   54 +-
 drivers/net/txgbe/base/txgbe_hw.h         |    4 +-
 drivers/net/txgbe/base/txgbe_osdep.h      |    4 +
 drivers/net/txgbe/base/txgbe_phy.c        |  362 +-
 drivers/net/txgbe/base/txgbe_phy.h        |   46 +-
 drivers/net/txgbe/base/txgbe_regs.h       |   13 +-
 drivers/net/txgbe/base/txgbe_type.h       |   43 +-
 drivers/net/txgbe/txgbe_ethdev.c          |  472 ++-
 drivers/net/txgbe/txgbe_ethdev.h          |    7 +-
 drivers/net/txgbe/txgbe_rxtx.c            |  109 +-
 drivers/net/txgbe/txgbe_rxtx.h            |   36 +
 drivers/net/txgbe/txgbe_rxtx_vec_common.h |   17 +-
 26 files changed, 9464 insertions(+), 444 deletions(-)
 create mode 100644 drivers/net/txgbe/base/txgbe_e56.c
 create mode 100644 drivers/net/txgbe/base/txgbe_e56.h
 create mode 100644 drivers/net/txgbe/base/txgbe_e56_bp.c
 create mode 100644 drivers/net/txgbe/base/txgbe_e56_bp.h

-- 
2.21.0.windows.1


^ permalink raw reply

* RE: [PATCH v8 12/21] net/txgbe: fix link stability for 25G NIC
From: Zaiyu Wang @ 2026-06-22 11:09 UTC (permalink / raw)
  To: 'Stephen Hemminger'; +Cc: dev
In-Reply-To: <20260617085355.0a87d7ee@phoenix.local>


> -----Original Message-----
> From: Stephen Hemminger <stephen@networkplumber.org>
> Sent: Wednesday, June 17, 2026 11:54 PM
> To: Zaiyu Wang <zaiyuwang@trustnetic.com>; Zaiyu Wang <zaiyuwang@trustnetic.com>
> Cc: dev@dpdk.org; stable@dpdk.org; Jiawen Wu <jiawenwu@trustnetic.com>; dev@dpdk.org;
> stable@dpdk.org; Jiawen Wu <jiawenwu@trustnetic.com>
> Subject: Re: [PATCH v8 12/21] net/txgbe: fix link stability for 25G NIC
> 
> On Wed, 17 Jun 2026 16:12:59 +0800
> Zaiyu Wang <zaiyuwang@trustnetic.com> wrote:
> 
> > +void
> > +set_fields_e56(unsigned int *src_data, unsigned int bit_high,
> > +	       unsigned int bit_low, unsigned int set_value) {
> 
> Function could be static here?

Hi Stephen,
Thanks for your time. This function is used in both txgbe_e56.c (for general PHY
configuration) and txgbe_e56_bp.c (for backplane mode configuration). Therefore, making it
static would not be feasible?
I have also fixed the other issues you pointed out, including the spelling corrections,
replacing tabs with spaces in log messages, and removing the term "master" from comments.

Best regards,
Zaiyu


^ permalink raw reply

* Re: [PATCH v2 09/10] bus/vmbus: store name in bus specific device
From: Bruce Richardson @ 2026-06-22 10:28 UTC (permalink / raw)
  To: David Marchand
  Cc: dev, thomas, stephen, fengchengwen, longli, hemant.agrawal,
	Wei Hu
In-Reply-To: <20260618152826.490569-10-david.marchand@redhat.com>

On Thu, Jun 18, 2026 at 05:28:24PM +0200, David Marchand wrote:
> The device name is allocated with strdup() during scan and freed in
> several places. However, when this bus cleanup is converted to use the
> EAL generic helper, freeing the device object will require a custom
> helper to also free the device name (and for this, a cast will be
> needed).
> 
> Instead, add an embedded name array to rte_vmbus_device structure
> (char name[RTE_DEV_NAME_MAX_LEN]) which is sufficient for all VMBUS
> device names (UUID format: 36 characters, or shorter legacy format).
> 
> This simplifies the device freeing to a simple free() call.
> 
> Signed-off-by: David Marchand <david.marchand@redhat.com>
> ---

Seems reasonable to me.
Acked-by: Bruce Richardson <bruce.richardson@intel.com>


^ permalink raw reply

* Re: [PATCH v2 08/10] bus: implement cleanup in EAL
From: Bruce Richardson @ 2026-06-22 10:26 UTC (permalink / raw)
  To: David Marchand
  Cc: dev, thomas, stephen, fengchengwen, longli, hemant.agrawal,
	Parav Pandit, Xueming Li, Sachin Saxena, Rosen Xu, Chenbo Xia,
	Nipun Gupta, Tomasz Duszynski, Wei Hu
In-Reply-To: <20260618152826.490569-9-david.marchand@redhat.com>

On Thu, Jun 18, 2026 at 05:28:23PM +0200, David Marchand wrote:
> Introduce a generic cleanup helper rte_bus_generic_cleanup() that
> eliminates code duplication across bus cleanup implementations:
> unplug probed devices, remove devargs, remove from bus list,
> and free device structures.
> 
> Add .free_device operation to struct rte_bus to allow buses to specify
> how to free their device structures.
> Update all buses for the new .cleanup and RTE_REGISTER_BUS prototypes.
> 
> Convert to rte_bus_generic_cleanup() the buses that have both a .cleanup
> and .unplug_device: this requires implementing .free_device for them.
> 
> Untouched buses are:
> - dma/idxd which has no unplug support,
> - bus/cdx which has unplug support, but no cleanup was implemented so
>   far,
> - NXP buses:
>   - bus/dpaa and bus/fslmc have many issues on interrupt
>     allocation/setup/freeing or VFIO setup/release,
>   - bus/fslmc cleanup callback is actually implemented in its internal
>     VFIO layer and requires too much refactoring,
> 
> Signed-off-by: David Marchand <david.marchand@redhat.com>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>

^ permalink raw reply

* Re: [PATCH v2 07/10] bus: align unplug with device probe
From: Bruce Richardson @ 2026-06-22 10:19 UTC (permalink / raw)
  To: David Marchand
  Cc: dev, thomas, stephen, fengchengwen, longli, hemant.agrawal,
	Parav Pandit, Xueming Li, Nipun Gupta, Nikhil Agarwal,
	Sachin Saxena, Rosen Xu, Chenbo Xia, Tomasz Duszynski
In-Reply-To: <20260618152826.490569-8-david.marchand@redhat.com>

On Thu, Jun 18, 2026 at 05:28:22PM +0200, David Marchand wrote:
> Refactor bus unplug operations to be the counterpart of probe_device.
> The (renamed) unplug operation now only handles:
> - Driver removal (calling the driver's remove callback)
> - Freeing probe-allocated resources (interrupts, mappings)
> 
> Device deletion (devargs removal, bus removal, freeing device
> structure) is now handled only during bus cleanup, not in unplug.
> 
> Additionally, move driver pointer clearing from individual bus unplug
> operations to EAL's local_dev_remove() where the unplug operation is
> invoked. This centralizes driver lifecycle management and eliminates
> code duplication across bus drivers.
> 
> For vdev, add a check in rte_vdev_uninit() since this public API can
> be called on devices without a driver attached.
> 
> Signed-off-by: David Marchand <david.marchand@redhat.com>

Running an AI correctness review reports a potential issue with ifpga bus
after this patch. Maybe worth a double check.

/Bruce

In ifpga_alloc_afu_dev (called at scan time, before any probe),
afu_dev->intr_handle is allocated unconditionally.

In the old ifpga_cleanup, the "goto free:" label ran for all devices, so
intr_handle was freed unconditionally. In the new ifpga_cleanup,
intr_handle is only freed via ifpga_unplug_device, which is only called
when rte_dev_is_probed() returns true. For any ifpga device discovered
during scan but never successfully probed (no matching driver, failed
probe, blocked devargs), intr_handle is leaked. The fix should free it
unconditionally in ifpga_cleanup, outside the rte_dev_is_probed block.

> ---
>  doc/guides/prog_guide/device_hotplug.rst | 18 ++++---
>  drivers/bus/auxiliary/auxiliary_common.c | 46 ++++++----------
>  drivers/bus/cdx/cdx.c                    | 29 ++--------
>  drivers/bus/fslmc/fslmc_bus.c            |  7 +--
>  drivers/bus/ifpga/ifpga_bus.c            | 63 ++++++++++------------
>  drivers/bus/pci/pci_common.c             | 57 ++++----------------
>  drivers/bus/platform/platform.c          | 16 +++---
>  drivers/bus/uacce/uacce.c                | 67 ++++++++----------------
>  drivers/bus/vdev/vdev.c                  | 53 ++++++++-----------
>  lib/eal/common/eal_common_dev.c          |  8 +--
>  lib/eal/include/bus_driver.h             |  4 +-
>  11 files changed, 129 insertions(+), 239 deletions(-)
> 
> diff --git a/doc/guides/prog_guide/device_hotplug.rst b/doc/guides/prog_guide/device_hotplug.rst
> index 7eb7fbcc2b..d21ba0c244 100644
> --- a/doc/guides/prog_guide/device_hotplug.rst
> +++ b/doc/guides/prog_guide/device_hotplug.rst
> @@ -165,7 +165,7 @@ using ``rte_dev_event_callback_register()`` function.
>     on the device in question.
>     When ``RTE_DEV_EVENT_REMOVE`` event is delivered,
>     it indicates that the kernel has removed the device;
> -   the application should call ``rte_dev_remove()`` to clean up EAL resources.
> +   the application should call ``rte_dev_remove()`` to unplug the device driver.
>  
>  
>  Event Notification Usage
> @@ -256,13 +256,17 @@ When ``rte_dev_remove()`` is called, the following sequence occurs:
>     See `Multi-process Synchronization`_ for details.
>  
>  #. **Device Unplug**:
> -   The bus's ``unplug()`` method is called (``dev->bus->unplug()``),
> -   which triggers the driver's remove function.
> -   This typically stops device operations, releases device resources,
> -   unmaps memory regions, and unregisters from subsystems.
> +   The bus's ``unplug_device()`` method is called (``dev->bus->unplug_device()``),
> +   which triggers the driver's remove function
> +   and releases resources allocated during probe
> +   (such as interrupt handles and device memory mappings).
>  
> -#. **Devargs Cleanup**:
> -   The devargs associated with the device are removed from the global list.
> +.. note::
> +
> +   The device structure, its devargs, and its entry in the bus device list
> +   are NOT freed during ``rte_dev_remove()``.
> +   They remain in memory until ``rte_eal_cleanup()`` is called,
> +   at which point the bus's ``cleanup()`` method handles complete device deletion.
>  
>  
>  Multi-process Synchronization
> diff --git a/drivers/bus/auxiliary/auxiliary_common.c b/drivers/bus/auxiliary/auxiliary_common.c
> index 048aacf254..10f466e57a 100644
> --- a/drivers/bus/auxiliary/auxiliary_common.c
> +++ b/drivers/bus/auxiliary/auxiliary_common.c
> @@ -122,13 +122,11 @@ auxiliary_probe_device(struct rte_driver *drv, struct rte_device *dev)
>  	return ret;
>  }
>  
> -/*
> - * Call the remove() function of the driver.
> - */
>  static int
> -rte_auxiliary_driver_remove_dev(struct rte_auxiliary_device *dev)
> +auxiliary_unplug_device(struct rte_device *rte_dev)
>  {
> -	const struct rte_auxiliary_driver *drv = RTE_BUS_DRIVER(dev->device.driver, *drv);
> +	const struct rte_auxiliary_driver *drv = RTE_BUS_DRIVER(rte_dev->driver, *drv);
> +	struct rte_auxiliary_device *dev = RTE_BUS_DEVICE(rte_dev, *dev);
>  	int ret = 0;
>  
>  	AUXILIARY_LOG(DEBUG, "Driver %s remove auxiliary device %s on NUMA node %i",
> @@ -140,8 +138,8 @@ rte_auxiliary_driver_remove_dev(struct rte_auxiliary_device *dev)
>  			return ret;
>  	}
>  
> -	/* clear driver structure */
> -	dev->device.driver = NULL;
> +	rte_intr_instance_free(dev->intr_handle);
> +	dev->intr_handle = NULL;
>  
>  	return 0;
>  }
> @@ -181,22 +179,6 @@ rte_auxiliary_unregister(struct rte_auxiliary_driver *driver)
>  	rte_bus_remove_driver(&auxiliary_bus, &driver->driver);
>  }
>  
> -static int
> -auxiliary_unplug(struct rte_device *dev)
> -{
> -	struct rte_auxiliary_device *adev = RTE_BUS_DEVICE(dev, *adev);
> -	int ret;
> -
> -	ret = rte_auxiliary_driver_remove_dev(adev);
> -	if (ret == 0) {
> -		rte_bus_remove_device(&auxiliary_bus, &adev->device);
> -		rte_devargs_remove(dev->devargs);
> -		rte_intr_instance_free(adev->intr_handle);
> -		free(adev);
> -	}
> -	return ret;
> -}
> -
>  static int
>  auxiliary_cleanup(void)
>  {
> @@ -206,13 +188,17 @@ auxiliary_cleanup(void)
>  	RTE_BUS_FOREACH_DEV(dev, &auxiliary_bus) {
>  		int ret;
>  
> -		if (!rte_dev_is_probed(&dev->device))
> -			continue;
> -		ret = auxiliary_unplug(&dev->device);
> -		if (ret < 0) {
> -			rte_errno = errno;
> -			error = -1;
> +		if (rte_dev_is_probed(&dev->device)) {
> +			ret = auxiliary_unplug_device(&dev->device);
> +			if (ret < 0) {
> +				rte_errno = errno;
> +				error = -1;
> +			}
>  		}
> +
> +		rte_devargs_remove(dev->device.devargs);
> +		rte_bus_remove_device(&auxiliary_bus, &dev->device);
> +		free(dev);
>  	}
>  
>  	return error;
> @@ -265,7 +251,7 @@ struct rte_bus auxiliary_bus = {
>  	.find_device = rte_bus_generic_find_device,
>  	.match = auxiliary_bus_match,
>  	.probe_device = auxiliary_probe_device,
> -	.unplug = auxiliary_unplug,
> +	.unplug_device = auxiliary_unplug_device,
>  	.parse = auxiliary_parse,
>  	.dma_map = auxiliary_dma_map,
>  	.dma_unmap = auxiliary_dma_unmap,
> diff --git a/drivers/bus/cdx/cdx.c b/drivers/bus/cdx/cdx.c
> index 2443161e1a..c0b46a41ad 100644
> --- a/drivers/bus/cdx/cdx.c
> +++ b/drivers/bus/cdx/cdx.c
> @@ -374,14 +374,11 @@ rte_cdx_unregister(struct rte_cdx_driver *driver)
>  	rte_bus_remove_driver(&rte_cdx_bus, &driver->driver);
>  }
>  
> -/*
> - * If vendor/device ID match, call the remove() function of the
> - * driver.
> - */
>  static int
> -cdx_detach_dev(struct rte_cdx_device *dev)
> +cdx_unplug_device(struct rte_device *rte_dev)
>  {
> -	const struct rte_cdx_driver *dr = RTE_BUS_DRIVER(dev->device.driver, *dr);
> +	const struct rte_cdx_driver *dr = RTE_BUS_DRIVER(rte_dev->driver, *dr);
> +	struct rte_cdx_device *dev = RTE_BUS_DEVICE(rte_dev, *dev);
>  	int ret = 0;
>  
>  	CDX_BUS_DEBUG("detach device %s using driver: %s",
> @@ -393,9 +390,6 @@ cdx_detach_dev(struct rte_cdx_device *dev)
>  			return ret;
>  	}
>  
> -	/* clear driver structure */
> -	dev->device.driver = NULL;
> -
>  	rte_cdx_unmap_device(dev);
>  
>  	rte_intr_instance_free(dev->intr_handle);
> @@ -404,21 +398,6 @@ cdx_detach_dev(struct rte_cdx_device *dev)
>  	return 0;
>  }
>  
> -static int
> -cdx_unplug(struct rte_device *dev)
> -{
> -	struct rte_cdx_device *cdx_dev = RTE_BUS_DEVICE(dev, *cdx_dev);
> -	int ret;
> -
> -	ret = cdx_detach_dev(cdx_dev);
> -	if (ret == 0) {
> -		rte_bus_remove_device(&rte_cdx_bus, &cdx_dev->device);
> -		rte_devargs_remove(dev->devargs);
> -		free(cdx_dev);
> -	}
> -	return ret;
> -}
> -
>  static int
>  cdx_dma_map(struct rte_device *dev, void *addr, uint64_t iova, size_t len)
>  {
> @@ -452,7 +431,7 @@ static struct rte_bus rte_cdx_bus = {
>  	.find_device = rte_bus_generic_find_device,
>  	.match = cdx_bus_match,
>  	.probe_device = cdx_probe_device,
> -	.unplug = cdx_unplug,
> +	.unplug_device = cdx_unplug_device,
>  	.parse = cdx_parse,
>  	.dma_map = cdx_dma_map,
>  	.dma_unmap = cdx_dma_unmap,
> diff --git a/drivers/bus/fslmc/fslmc_bus.c b/drivers/bus/fslmc/fslmc_bus.c
> index c7549a361a..dca4c5b182 100644
> --- a/drivers/bus/fslmc/fslmc_bus.c
> +++ b/drivers/bus/fslmc/fslmc_bus.c
> @@ -520,6 +520,7 @@ fslmc_bus_probe_device(struct rte_driver *driver, struct rte_device *rte_dev)
>  		return 0;
>  	}
>  
> +	/* FIXME: probe_device should allocate intr_handle */
>  	ret = drv->probe(drv, dev);
>  	if (ret != 0) {
>  		DPAA2_BUS_ERR("Unable to probe");
> @@ -531,7 +532,7 @@ fslmc_bus_probe_device(struct rte_driver *driver, struct rte_device *rte_dev)
>  }
>  
>  static int
> -fslmc_bus_unplug(struct rte_device *rte_dev)
> +fslmc_bus_unplug_device(struct rte_device *rte_dev)
>  {
>  	struct rte_dpaa2_device *dev = RTE_BUS_DEVICE(rte_dev, *dev);
>  	const struct rte_dpaa2_driver *drv = RTE_BUS_DRIVER(rte_dev->driver, *drv);
> @@ -540,7 +541,7 @@ fslmc_bus_unplug(struct rte_device *rte_dev)
>  		int ret = drv->remove(dev);
>  		if (ret != 0)
>  			return ret;
> -		dev->device.driver = NULL;
> +		/* FIXME: unplug_device should free intr_handle */
>  		DPAA2_BUS_INFO("%s Un-Plugged",  dev->device.name);
>  		return 0;
>  	}
> @@ -558,7 +559,7 @@ struct rte_bus rte_fslmc_bus = {
>  	.get_iommu_class = rte_dpaa2_get_iommu_class,
>  	.match = fslmc_bus_match,
>  	.probe_device = fslmc_bus_probe_device,
> -	.unplug = fslmc_bus_unplug,
> +	.unplug_device = fslmc_bus_unplug_device,
>  	.dev_iterate = rte_bus_generic_dev_iterate,
>  };
>  
> diff --git a/drivers/bus/ifpga/ifpga_bus.c b/drivers/bus/ifpga/ifpga_bus.c
> index 2c22329f65..394b777916 100644
> --- a/drivers/bus/ifpga/ifpga_bus.c
> +++ b/drivers/bus/ifpga/ifpga_bus.c
> @@ -276,6 +276,25 @@ ifpga_probe_device(struct rte_driver *drv, struct rte_device *dev)
>  	return afu_drv->probe(afu_dev);
>  }
>  
> +static int
> +ifpga_unplug_device(struct rte_device *dev)
> +{
> +	const struct rte_afu_driver *afu_drv = RTE_BUS_DRIVER(dev->driver, *afu_drv);
> +	struct rte_afu_device *afu_dev = RTE_BUS_DEVICE(dev, *afu_dev);
> +	int ret = 0;
> +
> +	if (afu_drv->remove) {
> +		ret = afu_drv->remove(afu_dev);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	rte_intr_instance_free(afu_dev->intr_handle);
> +	afu_dev->intr_handle = NULL;
> +
> +	return 0;
> +}
> +
>  /*
>   * Cleanup the content of the Intel FPGA bus, and call the remove() function
>   * for all registered devices.
> @@ -287,52 +306,24 @@ ifpga_cleanup(void)
>  	int error = 0;
>  
>  	RTE_BUS_FOREACH_DEV(afu_dev, &rte_ifpga_bus) {
> -		const struct rte_afu_driver *drv;
>  		int ret = 0;
>  
> -		if (!rte_dev_is_probed(&afu_dev->device))
> -			goto free;
> -		drv = RTE_BUS_DRIVER(afu_dev->device.driver, *drv);
> -		if (drv->remove == NULL)
> -			goto free;
> -
> -		ret = drv->remove(afu_dev);
> -		if (ret < 0) {
> -			rte_errno = errno;
> -			error = -1;
> +		if (rte_dev_is_probed(&afu_dev->device)) {
> +			ret = ifpga_unplug_device(&afu_dev->device);
> +			if (ret < 0) {
> +				rte_errno = errno;
> +				error = -1;
> +			}
>  		}
> -		afu_dev->device.driver = NULL;
>  
> -free:
> -		rte_bus_remove_device(&rte_ifpga_bus, &afu_dev->device);
>  		rte_devargs_remove(afu_dev->device.devargs);
> -		rte_intr_instance_free(afu_dev->intr_handle);
> +		rte_bus_remove_device(&rte_ifpga_bus, &afu_dev->device);
>  		free(afu_dev);
>  	}
>  
>  	return error;
>  }
>  
> -static int
> -ifpga_unplug(struct rte_device *dev)
> -{
> -	struct rte_afu_device *afu_dev = RTE_BUS_DEVICE(dev, *afu_dev);
> -	const struct rte_afu_driver *afu_drv = RTE_BUS_DRIVER(dev->driver, *afu_drv);
> -	int ret;
> -
> -	ret = afu_drv->remove(afu_dev);
> -	if (ret)
> -		return ret;
> -
> -	rte_bus_remove_device(&rte_ifpga_bus, &afu_dev->device);
> -
> -	rte_devargs_remove(dev->devargs);
> -	rte_intr_instance_free(afu_dev->intr_handle);
> -	free(afu_dev);
> -	return 0;
> -
> -}
> -
>  static int
>  ifpga_parse(const char *name, void *addr)
>  {
> @@ -384,7 +375,7 @@ static struct rte_bus rte_ifpga_bus = {
>  	.find_device = rte_bus_generic_find_device,
>  	.match       = ifpga_bus_match,
>  	.probe_device = ifpga_probe_device,
> -	.unplug      = ifpga_unplug,
> +	.unplug_device = ifpga_unplug_device,
>  	.parse       = ifpga_parse,
>  };
>  
> diff --git a/drivers/bus/pci/pci_common.c b/drivers/bus/pci/pci_common.c
> index 791e9a7b49..bf4822f7ec 100644
> --- a/drivers/bus/pci/pci_common.c
> +++ b/drivers/bus/pci/pci_common.c
> @@ -282,13 +282,10 @@ pci_probe_device(struct rte_driver *drv, struct rte_device *dev)
>  	return ret;
>  }
>  
> -/*
> - * If vendor/device ID match, call the remove() function of the
> - * driver.
> - */
>  static int
> -rte_pci_detach_dev(struct rte_pci_device *dev)
> +pci_unplug_device(struct rte_device *rte_dev)
>  {
> +	struct rte_pci_device *dev = RTE_BUS_DEVICE(rte_dev, *dev);
>  	struct rte_pci_addr *loc;
>  	const struct rte_pci_driver *dr = RTE_BUS_DRIVER(dev->device.driver, *dr);
>  	int ret = 0;
> @@ -308,9 +305,6 @@ rte_pci_detach_dev(struct rte_pci_device *dev)
>  			return ret;
>  	}
>  
> -	/* clear driver structure */
> -	dev->device.driver = NULL;
> -
>  	if (dr->drv_flags & RTE_PCI_DRV_NEED_MAPPING)
>  		/* unmap resources for devices that use igb_uio */
>  		rte_pci_unmap_device(dev);
> @@ -330,33 +324,17 @@ pci_cleanup(void)
>  	int error = 0;
>  
>  	RTE_BUS_FOREACH_DEV(dev, &rte_pci_bus) {
> -		const struct rte_pci_driver *drv;
>  		int ret = 0;
>  
> -		if (!rte_dev_is_probed(&dev->device))
> -			goto free;
> -		drv = RTE_BUS_DRIVER(dev->device.driver, *drv);
> -		if (drv->remove == NULL)
> -			goto free;
> -
> -		ret = drv->remove(dev);
> -		if (ret < 0) {
> -			rte_errno = errno;
> -			error = -1;
> +		if (rte_dev_is_probed(&dev->device)) {
> +			ret = pci_unplug_device(&dev->device);
> +			if (ret < 0) {
> +				rte_errno = errno;
> +				error = -1;
> +			}
>  		}
>  
> -		if (drv->drv_flags & RTE_PCI_DRV_NEED_MAPPING)
> -			rte_pci_unmap_device(dev);
> -
> -		dev->device.driver = NULL;
> -
> -free:
> -		/* free interrupt handles */
> -		rte_intr_instance_free(dev->intr_handle);
> -		dev->intr_handle = NULL;
> -		rte_intr_instance_free(dev->vfio_req_intr_handle);
> -		dev->vfio_req_intr_handle = NULL;
> -
> +		rte_devargs_remove(dev->device.devargs);
>  		rte_bus_remove_device(&rte_pci_bus, &dev->device);
>  		pci_free(RTE_PCI_DEVICE_INTERNAL(dev));
>  	}
> @@ -521,21 +499,6 @@ pci_sigbus_handler(const void *failure_addr)
>  	return ret;
>  }
>  
> -static int
> -pci_unplug(struct rte_device *dev)
> -{
> -	struct rte_pci_device *pdev = RTE_BUS_DEVICE(dev, *pdev);
> -	int ret;
> -
> -	ret = rte_pci_detach_dev(pdev);
> -	if (ret == 0) {
> -		rte_bus_remove_device(&rte_pci_bus, &pdev->device);
> -		rte_devargs_remove(dev->devargs);
> -		pci_free(RTE_PCI_DEVICE_INTERNAL(pdev));
> -	}
> -	return ret;
> -}
> -
>  static int
>  pci_dma_map(struct rte_device *dev, void *addr, uint64_t iova, size_t len)
>  {
> @@ -784,7 +747,7 @@ struct rte_bus rte_pci_bus = {
>  	.find_device = rte_bus_generic_find_device,
>  	.match = pci_bus_match,
>  	.probe_device = pci_probe_device,
> -	.unplug = pci_unplug,
> +	.unplug_device = pci_unplug_device,
>  	.parse = pci_parse,
>  	.dev_compare = pci_dev_compare,
>  	.devargs_parse = rte_pci_devargs_parse,
> diff --git a/drivers/bus/platform/platform.c b/drivers/bus/platform/platform.c
> index 170a2e03d0..5b3c78a505 100644
> --- a/drivers/bus/platform/platform.c
> +++ b/drivers/bus/platform/platform.c
> @@ -416,19 +416,15 @@ device_release_driver(struct rte_platform_device *pdev)
>  		if (ret)
>  			PLATFORM_LOG_LINE(WARNING, "failed to remove %s", pdev->name);
>  	}
> -
> -	pdev->device.driver = NULL;
>  }
>  
>  static int
> -platform_bus_unplug(struct rte_device *dev)
> +platform_bus_unplug_device(struct rte_device *dev)
>  {
>  	struct rte_platform_device *pdev = RTE_BUS_DEVICE(dev, *pdev);
>  
>  	device_release_driver(pdev);
>  	device_cleanup(pdev);
> -	rte_devargs_remove(pdev->device.devargs);
> -	free(pdev);
>  
>  	return 0;
>  }
> @@ -501,10 +497,12 @@ platform_bus_cleanup(void)
>  	struct rte_platform_device *pdev;
>  
>  	RTE_BUS_FOREACH_DEV(pdev, &platform_bus) {
> +		if (rte_dev_is_probed(&pdev->device))
> +			platform_bus_unplug_device(&pdev->device);
> +
> +		rte_devargs_remove(pdev->device.devargs);
>  		rte_bus_remove_device(&platform_bus, &pdev->device);
> -		if (!rte_dev_is_probed(&pdev->device))
> -			continue;
> -		platform_bus_unplug(&pdev->device);
> +		free(pdev);
>  	}
>  
>  	return 0;
> @@ -516,7 +514,7 @@ static struct rte_bus platform_bus = {
>  	.find_device = rte_bus_generic_find_device,
>  	.match = platform_bus_match,
>  	.probe_device = platform_bus_probe_device,
> -	.unplug = platform_bus_unplug,
> +	.unplug_device = platform_bus_unplug_device,
>  	.parse = platform_bus_parse,
>  	.dma_map = platform_bus_dma_map,
>  	.dma_unmap = platform_bus_dma_unmap,
> diff --git a/drivers/bus/uacce/uacce.c b/drivers/bus/uacce/uacce.c
> index 8a3c55b248..bfe1f26557 100644
> --- a/drivers/bus/uacce/uacce.c
> +++ b/drivers/bus/uacce/uacce.c
> @@ -385,40 +385,10 @@ uacce_probe_device(struct rte_driver *drv, struct rte_device *dev)
>  }
>  
>  static int
> -uacce_cleanup(void)
> +uacce_unplug_device(struct rte_device *rte_dev)
>  {
> -	struct rte_uacce_device *dev;
> -	int error = 0;
> -
> -	RTE_BUS_FOREACH_DEV(dev, &uacce_bus) {
> -		const struct rte_uacce_driver *dr;
> -		int ret = 0;
> -
> -		if (!rte_dev_is_probed(&dev->device))
> -			goto free;
> -		dr = RTE_BUS_DRIVER(dev->device.driver, *dr);
> -		if (dr->remove == NULL)
> -			goto free;
> -
> -		ret = dr->remove(dev);
> -		if (ret < 0) {
> -			rte_errno = errno;
> -			error = -1;
> -		}
> -		dev->device.driver = NULL;
> -
> -free:
> -		rte_bus_remove_device(&uacce_bus, &dev->device);
> -		free(dev);
> -	}
> -
> -	return error;
> -}
> -
> -static int
> -uacce_detach_dev(struct rte_uacce_device *dev)
> -{
> -	const struct rte_uacce_driver *dr = RTE_BUS_DRIVER(dev->device.driver, *dr);
> +	const struct rte_uacce_driver *dr = RTE_BUS_DRIVER(rte_dev->driver, *dr);
> +	struct rte_uacce_device *dev = RTE_BUS_DEVICE(rte_dev, *dev);
>  	int ret = 0;
>  
>  	UACCE_BUS_DEBUG("detach device %s using driver: %s", dev->device.name, dr->driver.name);
> @@ -429,25 +399,32 @@ uacce_detach_dev(struct rte_uacce_device *dev)
>  			return ret;
>  	}
>  
> -	dev->device.driver = NULL;
> -
>  	return 0;
>  }
>  
>  static int
> -uacce_unplug(struct rte_device *dev)
> +uacce_cleanup(void)
>  {
> -	struct rte_uacce_device *uacce_dev = RTE_BUS_DEVICE(dev, *uacce_dev);
> -	int ret;
> +	struct rte_uacce_device *dev;
> +	int error = 0;
>  
> -	ret = uacce_detach_dev(uacce_dev);
> -	if (ret == 0) {
> -		rte_bus_remove_device(&uacce_bus, &uacce_dev->device);
> -		rte_devargs_remove(dev->devargs);
> -		free(uacce_dev);
> +	RTE_BUS_FOREACH_DEV(dev, &uacce_bus) {
> +		int ret = 0;
> +
> +		if (rte_dev_is_probed(&dev->device)) {
> +			ret = uacce_unplug_device(&dev->device);
> +			if (ret < 0) {
> +				rte_errno = errno;
> +				error = -1;
> +			}
> +		}
> +
> +		rte_devargs_remove(dev->device.devargs);
> +		rte_bus_remove_device(&uacce_bus, &dev->device);
> +		free(dev);
>  	}
>  
> -	return ret;
> +	return error;
>  }
>  
>  static int
> @@ -577,7 +554,7 @@ static struct rte_bus uacce_bus = {
>  	.cleanup = uacce_cleanup,
>  	.match = uacce_bus_match,
>  	.probe_device = uacce_probe_device,
> -	.unplug = uacce_unplug,
> +	.unplug_device = uacce_unplug_device,
>  	.find_device = rte_bus_generic_find_device,
>  	.parse = uacce_parse,
>  	.dev_iterate = rte_bus_generic_dev_iterate,
> diff --git a/drivers/bus/vdev/vdev.c b/drivers/bus/vdev/vdev.c
> index 09221ccdea..7e94f86e28 100644
> --- a/drivers/bus/vdev/vdev.c
> +++ b/drivers/bus/vdev/vdev.c
> @@ -343,19 +343,15 @@ rte_vdev_init(const char *name, const char *args)
>  }
>  
>  static int
> -vdev_remove_driver(struct rte_vdev_device *dev)
> +vdev_unplug_device(struct rte_device *rte_dev)
>  {
> -	const char *name = rte_vdev_device_name(dev);
> -	const struct rte_vdev_driver *driver;
> +	const struct rte_vdev_driver *driver = RTE_BUS_DRIVER(rte_dev->driver, *driver);
> +	struct rte_vdev_device *dev = RTE_BUS_DEVICE(rte_dev, *dev);
>  
> -	if (!dev->device.driver) {
> -		VDEV_LOG(DEBUG, "no driver attach to device %s", name);
> -		return 1;
> -	}
> +	if (driver->remove)
> +		return driver->remove(dev);
>  
> -	driver = RTE_BUS_DRIVER(dev->device.driver, *driver);
> -
> -	return driver->remove(dev);
> +	return 0;
>  }
>  
>  RTE_EXPORT_SYMBOL(rte_vdev_uninit)
> @@ -376,7 +372,12 @@ rte_vdev_uninit(const char *name)
>  		goto unlock;
>  	}
>  
> -	ret = vdev_remove_driver(dev);
> +	if (rte_dev_is_probed(&dev->device)) {
> +		ret = vdev_unplug_device(&dev->device);
> +	} else {
> +		VDEV_LOG(DEBUG, "no driver attach to device %s", name);
> +		ret = 1;
> +	}
>  	if (ret)
>  		goto unlock;
>  
> @@ -553,27 +554,21 @@ vdev_cleanup(void)
>  	struct rte_vdev_device *dev;
>  	int error = 0;
>  
> +	rte_spinlock_recursive_lock(&vdev_device_list_lock);
>  	RTE_BUS_FOREACH_DEV(dev, &rte_vdev_bus) {
> -		const struct rte_vdev_driver *drv;
>  		int ret;
>  
> -		if (!rte_dev_is_probed(&dev->device))
> -			goto free;
> -
> -		drv = RTE_BUS_DRIVER(dev->device.driver, *drv);
> -
> -		if (drv->remove == NULL)
> -			goto free;
> -
> -		ret = drv->remove(dev);
> -		if (ret < 0)
> -			error = -1;
> +		if (rte_dev_is_probed(&dev->device)) {
> +			ret = vdev_unplug_device(&dev->device);
> +			if (ret < 0)
> +				error = -1;
> +		}
>  
> -		dev->device.driver = NULL;
> -free:
> +		rte_devargs_remove(dev->device.devargs);
>  		rte_bus_remove_device(&rte_vdev_bus, &dev->device);
>  		free(dev);
>  	}
> +	rte_spinlock_recursive_unlock(&vdev_device_list_lock);
>  
>  	return error;
>  }
> @@ -591,12 +586,6 @@ vdev_find_device(const struct rte_bus *bus, const struct rte_device *start,
>  	return dev;
>  }
>  
> -static int
> -vdev_unplug(struct rte_device *dev)
> -{
> -	return rte_vdev_uninit(dev->name);
> -}
> -
>  static enum rte_iova_mode
>  vdev_get_iommu_class(void)
>  {
> @@ -623,7 +612,7 @@ static struct rte_bus rte_vdev_bus = {
>  	.find_device = vdev_find_device,
>  	.match = vdev_bus_match,
>  	.probe_device = vdev_probe_device,
> -	.unplug = vdev_unplug,
> +	.unplug_device = vdev_unplug_device,
>  	.parse = vdev_parse,
>  	.dma_map = vdev_dma_map,
>  	.dma_unmap = vdev_dma_unmap,
> diff --git a/lib/eal/common/eal_common_dev.c b/lib/eal/common/eal_common_dev.c
> index 2a2103ec57..762ed09e21 100644
> --- a/lib/eal/common/eal_common_dev.c
> +++ b/lib/eal/common/eal_common_dev.c
> @@ -385,19 +385,21 @@ local_dev_remove(struct rte_device *dev)
>  {
>  	int ret;
>  
> -	if (dev->bus->unplug == NULL) {
> -		EAL_LOG(ERR, "Function unplug not supported by bus (%s)",
> +	if (dev->bus->unplug_device == NULL) {
> +		EAL_LOG(ERR, "Function unplug_device not supported by bus (%s)",
>  			dev->bus->name);
>  		return -ENOTSUP;
>  	}
>  
> -	ret = dev->bus->unplug(dev);
> +	ret = dev->bus->unplug_device(dev);
>  	if (ret) {
>  		EAL_LOG(ERR, "Driver cannot detach the device (%s)",
>  			dev->name);
>  		return (ret < 0) ? ret : -ENOENT;
>  	}
>  
> +	dev->driver = NULL;
> +
>  	return 0;
>  }
>  
> diff --git a/lib/eal/include/bus_driver.h b/lib/eal/include/bus_driver.h
> index 9711e6712b..fde55ff06d 100644
> --- a/lib/eal/include/bus_driver.h
> +++ b/lib/eal/include/bus_driver.h
> @@ -101,7 +101,7 @@ typedef int (*rte_bus_probe_device_t)(struct rte_driver *drv, struct rte_device
>   *	0 on success.
>   *	!0 on error.
>   */
> -typedef int (*rte_bus_unplug_t)(struct rte_device *dev);
> +typedef int (*rte_bus_unplug_device_t)(struct rte_device *dev);
>  
>  /**
>   * Bus specific parsing function.
> @@ -323,7 +323,7 @@ struct rte_bus {
>  	rte_bus_find_device_t find_device; /**< Find a device on the bus */
>  	rte_bus_match_t match;       /**< Check if driver matches device */
>  	rte_bus_probe_device_t probe_device; /**< Probe single device with driver */
> -	rte_bus_unplug_t unplug;     /**< Remove single device from driver */
> +	rte_bus_unplug_device_t unplug_device; /**< Remove single device from driver */
>  	rte_bus_parse_t parse;       /**< Parse a device name */
>  	rte_bus_dev_compare_t dev_compare; /**< Compare two device names */
>  	rte_bus_devargs_parse_t devargs_parse; /**< Parse bus devargs */
> -- 
> 2.53.0
> 

^ permalink raw reply

* Re: [PATCH v2 01/10] bus: fix reference to plug callback
From: Bruce Richardson @ 2026-06-22  9:49 UTC (permalink / raw)
  To: David Marchand; +Cc: dev, thomas, stephen, fengchengwen, longli, hemant.agrawal
In-Reply-To: <20260618152826.490569-2-david.marchand@redhat.com>

On Thu, Jun 18, 2026 at 05:28:16PM +0200, David Marchand wrote:
> Remove now unused typedef, update documentation
> and some log following the callback rename.
> 
> Fixes: 76622feba9e6 ("bus: refactor device probe")
> 
> Signed-off-by: David Marchand <david.marchand@redhat.com>
> ---
> Changes since v1:
> - remove missed rte_bus_plug_t typedef,
> 
Acked-by: Bruce Richardson <bruce.richardson@intel.com>

^ permalink raw reply

* Re: [PATCH v2 02/10] dma/idxd: remove next pointer in bus specific device
From: Bruce Richardson @ 2026-06-22  9:48 UTC (permalink / raw)
  To: David Marchand
  Cc: dev, thomas, stephen, fengchengwen, longli, hemant.agrawal,
	Kevin Laatz
In-Reply-To: <20260618152826.490569-3-david.marchand@redhat.com>

On Thu, Jun 18, 2026 at 05:28:17PM +0200, David Marchand wrote:
> The dma/idxd devices are now stored in a list of generic rte_device
> objects.
> 
> Fixes: b4f0974a995b ("bus: factorize device list")
> 
> Signed-off-by: David Marchand <david.marchand@redhat.com>
> ---
>  drivers/dma/idxd/idxd_bus.c | 1 -
>  1 file changed, 1 deletion(-)
> 
Acked-by: Bruce Richardson <bruce.richardson@intel.com>

^ permalink raw reply

* Re: [PATCH 3/3] ml/cnxk: updated checks for number of MRVL layers
From: Jerin Jacob @ 2026-06-22  9:30 UTC (permalink / raw)
  To: Srikanth Yalavarthi; +Cc: dev, jerinj, sshankarnara, ptakkar, aprabhu
In-Reply-To: <20260609044202.936153-3-syalavarthi@marvell.com>

On Tue, Jun 9, 2026 at 10:12 AM Srikanth Yalavarthi
<syalavarthi@marvell.com> wrote:
>
> Add checks to validate the current active MRVL layers
> and increased the maximum number of layers supported
> to 512 for LLVM only models.
>
> Signed-off-by: Srikanth Yalavarthi <syalavarthi@marvell.com>

Series applied to dpdk-next-net-mrvl/for-main. Thanks

^ permalink raw reply

* Re: [PATCH 2/2] test/dma: add functions to verify zero and one fill
From: Bruce Richardson @ 2026-06-22  9:27 UTC (permalink / raw)
  To: Tejasree Kondoj
  Cc: Akhil Goyal, Chengwen Feng, Kevin Laatz, Vidya Sagar Velumuri,
	Anoob Joseph, dev
In-Reply-To: <20260622030623.86094-3-ktejasree@marvell.com>

On Mon, Jun 22, 2026 at 08:36:23AM +0530, Tejasree Kondoj wrote:
> Add test cases to verify zero fill and one fill
> 
> Signed-off-by: Vidya Sagar Velumuri <vvelumuri@marvell.com>
> ---
>  app/test/test.h        |  4 +++
>  app/test/test_dmadev.c | 57 ++++++++++++++++++++++++------------------
>  2 files changed, 37 insertions(+), 24 deletions(-)
> 

Not a bad idea. One piece of feedback inline below. Otherwise:

Acked-by: Bruce Richardson <bruce.richardson@intel.com>


> diff --git a/app/test/test.h b/app/test/test.h
> index 1f12fc5397..5fc2d7d048 100644
> --- a/app/test/test.h
> +++ b/app/test/test.h
> @@ -28,6 +28,10 @@
>  
>  #include <rte_test.h>
>  
> +#ifndef ARRAY_SIZE
> +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
> +#endif
> +
>  #define TEST_ASSERT RTE_TEST_ASSERT
>  
>  #define TEST_ASSERT_EQUAL RTE_TEST_ASSERT_EQUAL
> diff --git a/app/test/test_dmadev.c b/app/test/test_dmadev.c
> index b30f2214e5..d4299e501d 100644
> --- a/app/test/test_dmadev.c
> +++ b/app/test/test_dmadev.c
> @@ -931,42 +931,51 @@ test_completion_handling(int16_t dev_id, uint16_t vchan)
>  static int
>  test_enqueue_fill(int16_t dev_id, uint16_t vchan)
>  {
> +	uint64_t pattern[3] = {0x0, 0xfedcba9876543210, 0xffffffffffffffff};
>  	const unsigned int lengths[] = {8, 64, 1024, 50, 100, 89};
> +	unsigned int i, j, k;
>  	struct rte_mbuf *dst;
>  	char *dst_data;
> -	uint64_t pattern = 0xfedcba9876543210;
> -	unsigned int i, j;
>  
>  	dst = rte_pktmbuf_alloc(pool);
>  	if (dst == NULL)
>  		ERR_RETURN("Failed to allocate mbuf\n");
>  	dst_data = rte_pktmbuf_mtod(dst, char *);
>  
> -	for (i = 0; i < RTE_DIM(lengths); i++) {
> -		/* reset dst_data */
> -		memset(dst_data, 0, rte_pktmbuf_data_len(dst));
> +	for (k = 0; k < ARRAY_SIZE(pattern); k++) {
> +		for (i = 0; i < RTE_DIM(lengths); i++) {
> +			/* reset dst_data */
> +			memset(dst_data, 0, rte_pktmbuf_data_len(dst));
> +

I don't think we should reset between each run, because of the zero-fill
operation, which we cannot detect if it runs properly. I'd suggest
therefore:
* zero the buffer outside the main loop
* Run the non-zero patterns first, checking each one, without reset
* Do the zero-pattern at the end, so we can see that zeroing works.

> +			/* perform the fill operation */
> +			int id = rte_dma_fill(dev_id, vchan, pattern[k],
> +					rte_pktmbuf_iova(dst), lengths[i], RTE_DMA_OP_FLAG_SUBMIT);
> +			if (id < 0) {
> +				if (id == -ENOTSUP) {
> +					rte_pktmbuf_free(dst);
> +					return 0;
> +				}
> +				ERR_RETURN("Error with rte_dma_fill\n");
> +			}
> +			await_hw(dev_id, vchan);
>  
> -		/* perform the fill operation */
> -		int id = rte_dma_fill(dev_id, vchan, pattern,
> -				rte_pktmbuf_iova(dst), lengths[i], RTE_DMA_OP_FLAG_SUBMIT);
> -		if (id < 0)
> -			ERR_RETURN("Error with rte_dma_fill\n");
> -		await_hw(dev_id, vchan);
> +			if (rte_dma_completed(dev_id, vchan, 1, NULL, NULL) != 1)
> +				ERR_RETURN("Error: fill operation failed (length: %u)\n",
> +					   lengths[i]);
> +			/* check the data from the fill operation is correct */
> +			for (j = 0; j < lengths[i]; j++) {
> +				char pat_byte = ((char *)&pattern[k])[j % 8];
>  
> -		if (rte_dma_completed(dev_id, vchan, 1, NULL, NULL) != 1)
> -			ERR_RETURN("Error: fill operation failed (length: %u)\n", lengths[i]);
> -		/* check the data from the fill operation is correct */
> -		for (j = 0; j < lengths[i]; j++) {
> -			char pat_byte = ((char *)&pattern)[j % 8];
> -			if (dst_data[j] != pat_byte)
> -				ERR_RETURN("Error with fill operation (lengths = %u): got (%x), not (%x)\n",
> -						lengths[i], dst_data[j], pat_byte);
> +				if (dst_data[j] != pat_byte)
> +					ERR_RETURN("Error with fill operation (lengths = %u): got (%x), not (%x)\n",
> +							lengths[i], dst_data[j], pat_byte);
> +			}
> +			/* check that the data after the fill operation was not written to */
> +			for (; j < rte_pktmbuf_data_len(dst); j++)
> +				if (dst_data[j] != 0)
> +					ERR_RETURN("Error, fill operation wrote too far (lengths = %u): got (%x), not (%x)\n",
> +							lengths[i], dst_data[j], 0);
>  		}
> -		/* check that the data after the fill operation was not written to */
> -		for (; j < rte_pktmbuf_data_len(dst); j++)
> -			if (dst_data[j] != 0)
> -				ERR_RETURN("Error, fill operation wrote too far (lengths = %u): got (%x), not (%x)\n",
> -						lengths[i], dst_data[j], 0);
>  	}
>  
>  	rte_pktmbuf_free(dst);
> -- 
> 2.34.1
> 

^ permalink raw reply

* [PATCH v5 23/23] net/sxe2: update sxe2 feature matrix docs
From: liujie5 @ 2026-06-22  9:27 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260622092324.3091185-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

Update the sxe2.ini feature sheet to accurately reflect the recently
implemented hardware capabilities in the sxe2 PMD.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 doc/guides/nics/features/sxe2.ini |  56 ++++++++++
 doc/guides/nics/sxe2.rst          | 164 ++++++++++++++++++++++++++++++
 2 files changed, 220 insertions(+)

diff --git a/doc/guides/nics/features/sxe2.ini b/doc/guides/nics/features/sxe2.ini
index 09ba2f558c..3c1e6a8a39 100644
--- a/doc/guides/nics/features/sxe2.ini
+++ b/doc/guides/nics/features/sxe2.ini
@@ -7,17 +7,73 @@
 ; is selected.
 ;
 [Features]
+Speed capabilities   = Y
+Link status          = Y
+Link status event    = Y
+Rx interrupt         = Y
 Fast mbuf free       = P
 Free Tx mbuf on demand = Y
 Burst mode info      = Y
 Queue start/stop     = Y
+Power mgmt address monitor = Y
 Buffer split on Rx   = P
 Scattered Rx         = Y
+Traffic manager      = Y
 CRC offload          = Y
+VLAN offload         = Y
+QinQ offload         = P
 L3 checksum offload  = Y
 L4 checksum offload  = Y
+Timestamp offload    = P
+Inner L3 checksum    = P
+Inner L4 checksum    = P
 Rx descriptor status = Y
 Tx descriptor status = Y
+MTU update           = Y
+TSO                  = P
+Promiscuous mode     = Y
+Allmulticast mode    = Y
+Unicast MAC filter   = Y
+RSS hash             = Y
+RSS key update       = Y
+RSS reta update      = Y
+VLAN filter          = Y
+Inline crypto        = Y
+Packet type parsing  = Y
+Timesync             = Y
+Basic stats          = Y
+Extended stats       = Y
+FW version           = Y
+Module EEPROM dump   = Y
+Multiprocess aware   = Y
 Linux                = Y
 x86-32               = Y
 x86-64               = Y
+
+[rte_flow items]
+eth                  = P
+geneve               = Y
+gre                  = Y
+gtpu                 = Y
+ipv4                 = Y
+ipv6                 = Y
+ipv6_frag_ext        = Y
+nvgre                = Y
+sctp                 = Y
+tcp                  = Y
+udp                  = Y
+vlan                 = P
+vxlan                = Y
+vxlan_gpe            = Y
+
+[rte_flow actions]
+count                = Y
+drop                 = Y
+mark                 = Y
+passthru             = Y
+port_representor     = Y
+queue                = Y
+represented_port     = Y
+rss                  = Y
+send_to_kernel       = Y
+port_id              = Y
diff --git a/doc/guides/nics/sxe2.rst b/doc/guides/nics/sxe2.rst
index 539072b076..06541bf66e 100644
--- a/doc/guides/nics/sxe2.rst
+++ b/doc/guides/nics/sxe2.rst
@@ -35,3 +35,167 @@ preventing unauthorized access to random physical memory.
 This capability allows the PMD to coexist with kernel network interfaces
 which remain functional, although they stop receiving unicast packets
 as long as they share the same MAC address.
+
+Configuration
+-------------
+
+Runtime Configuration
+~~~~~~~~~~~~~~~~~~~~~
+
+- ``Traffic Management Scheduling Levels``
+
+  The DPDK Traffic Management (rte_tm) APIs can be used to configure the Tx scheduler on the NIC.
+  The ``sched-layer-mode`` parameter can be used to set the number of scheduling levels
+  in the transmit scheduling hierarchy.
+  The provided value must be between 0 and 3.
+  If the value provided is greater than the number of levels supported by the HW,
+  the driver will use the hardware maximum value.
+
+- ``flow-duplicate-pattern`` parameter [int]
+
+  There are two options to choose:
+
+  - 0. Prevent insertion of flow rules with the same pattern items.
+    In this case, duplicate rules are rejected and error code EEXIST is returned.
+
+  - 1. Allow insertion of flow rules with the same pattern items.
+    In this case, the per-rule metadata flag ``switch_pattern_dup_allow`` is set
+    to instruct the hardware switch engine that rules with identical pattern
+    items are permitted.
+
+  This option only applies to the switch engine flow type.
+  For the Fnav flow engine type, duplicate rules are always rejected.
+
+  By default, the PMD will set this value to 1.
+
+- ``fnav-stat-type`` parameter [int]
+
+  This parameter controls the Fnav flow engine statistics type used
+  for flow rule hit counting (via ``rte_flow_query``).
+
+  - 1: Only count the number of packets.
+  - 2: Only count the number of bytes.
+  - 3: Count both packets and bytes (default).
+
+  Default value is 3 (count both packets and bytes).
+
+- ``drv-sw-stats`` parameter [int]
+
+  This parameter controls whether per-packet software statistics
+  (SW stats) are collected in the Rx data path.
+
+  Hardware packet statistic counters may be inaccurate for certain
+  packet types due to hardware design limitations.
+  When accuracy of Rx packet classification statistics is critical,
+  enabling this parameter allows the driver to accumulate statistics
+  in software as packets are received, providing an alternative
+  statistical path that bypasses hardware counter inaccuracies.
+
+  - 0: Disable software statistics collection (default).
+    The basic port statistics (``ipackets``, ``ibytes``) are reported
+    from the hardware counters.
+  - 1: Enable software statistics collection.
+    Per-packet software statistics are accumulated for unicast,
+    multicast, broadcast, and dropped packets in the Rx data path.
+
+  When enabled, the following extended statistics (xstats) are available:
+  ``rx_sw_unicast_packets``, ``rx_sw_multicast_packets``,
+  ``rx_sw_broadcast_packets``, ``rx_sw_drop_packets``,
+  and ``rx_sw_drop_bytes``.
+
+- ``no-sched-mode`` parameter [int]
+
+  This parameter enables non-scheduling mode (no-sched mode).
+  When enabled, the transmit path bypasses the hardware scheduling module
+  and packets are sent directly out through the port.
+  This results in lower transmit latency and higher throughput,
+  but Traffic Management (rte_tm) APIs are not supported in this mode.
+
+  - 0: Disable non-scheduling mode (default).
+    The transmit path goes through the hardware scheduling hierarchy.
+    Traffic Management (rte_tm) APIs can be used to configure the Tx scheduler.
+  - 1: Enable non-scheduling mode.
+    The transmit path bypasses the hardware scheduling module.
+    Packets are sent directly from the port at full speed without scheduling.
+    Traffic Management (rte_tm) APIs are not available in this mode.
+
+- ``rx-low-latency`` parameter [int]
+
+  This parameter controls the interrupt throttling (ITR) interval
+  for Rx queue interrupts.
+
+  When enabled, the driver sets a shorter interrupt coalescing timeout
+  (``SXE2_ITR_INTERVAL_LOW``, approximately 1 μs),
+  reducing the time between packet arrival and interrupt delivery to the CPU.
+  This lowers receive latency at the cost of increased CPU interrupt rate.
+
+  When disabled (default), the driver uses the normal interrupt throttling
+  interval (``SXE2_ITR_INTERVAL_NORMAL``, approximately 20 μs),
+  which reduces the CPU interrupt rate at the expense of higher receive latency.
+
+  - 0: Disable Rx low latency (default).
+    Normal interrupt throttling interval (~20 μs) is used.
+  - 1: Enable Rx low latency.
+    Low interrupt throttling interval (~1 μs) is used
+    for reduced receive latency.
+
+- ``function-flow-direct`` parameter [int]
+
+  This parameter controls whether flow rules from different functional units
+  (DPDK vs kernel driver) are isolated or combined when both drivers
+  control the same physical port.
+
+  When the DPDK PMD and the kernel network driver coexist on the same port,
+  flow rules may originate from either driver.
+  This parameter determines how the source VSI (Virtual Switch Interface)
+  of each flow rule is handled during hardware programming.
+
+  - 0 (default): Isolate flow rules between DPDK and kernel.
+    When ``flow_isolated`` is enabled (``rte_flow_isolate()`` called),
+    kernel-side flow rules take priority and DPDK-side flow rules are suppressed.
+    When ``flow_isolated`` is disabled, DPDK-side flow rules take priority
+    and kernel-side flow rules are suppressed.
+    Only one functional unit's flows are active at a time.
+
+  - 1: Allow direct flow rules from both DPDK and kernel simultaneously.
+    Both DPDK and kernel source VSIs are preserved in the hardware flow table.
+    Flow rules from both sides are programmed without isolation.
+
+  This option only applies to FNAV and ACL flow engine types
+  and does not apply to PF bond devices.
+
+Extended Statistics (xstats)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The PMD provides the following extended statistics (xstats) for detailed
+monitoring of receive-side packet classification and software-level accounting.
+The software statistics path is provided as a workaround for hardware
+counter inaccuracies on certain packet types --- it accumulates per-packet
+statistics directly in the Rx data path, ensuring that unicast, multicast,
+broadcast, and drop counts reflect the actual packets processed by the driver.
+
+Receive Software Statistics
+  These counters are collected in the Rx data path when ``drv-sw-stats=1``
+  is configured (see the ``drv-sw-stats`` devarg above).
+  When ``drv-sw-stats`` is disabled (default), these xstats report zero.
+
+  - ``rx_sw_unicast_packets``: Number of unicast packets received.
+  - ``rx_sw_multicast_packets``: Number of multicast packets received.
+  - ``rx_sw_broadcast_packets``: Number of broadcast packets received.
+  - ``rx_sw_drop_packets``: Number of packets dropped in the Rx data path.
+  - ``rx_sw_drop_bytes``: Number of bytes dropped in the Rx data path.
+
+  When ``drv-sw-stats`` is enabled, the basic counters ``ipackets`` and
+  ``ibytes`` (from ``rte_eth_stats``) also reflect the software-accumulated
+  packet and byte counts. Otherwise, they are reported from hardware counters.
+
+Fnav Flow Engine Statistics
+  The Fnav flow engine statistics type is controlled by the ``fnav-stat-type``
+  devarg (see above). Depending on the configuration:
+
+  - ``fnav-stat-type=1``: Only packet count is available.
+  - ``fnav-stat-type=2``: Only byte count is available.
+  - ``fnav-stat-type=3`` (default): Both packet and byte counts are available.
+
+  Flow query results (via ``rte_flow_query``) expose these per-flow counters
+  through the query API, not via xstats.
-- 
2.52.0


^ permalink raw reply related

* [PATCH v5 22/23] net/sxe2: add private devargs parsing
From: liujie5 @ 2026-06-22  9:27 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260622092324.3091185-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

Introduce runtime private device arguments (devargs) support for the
sxe2 PMD. This allows users to customize driver behavior at startup
without recompiling the source code.

The parameters are parsed using the standard 'rte_kvargs' library during
the PCI/vdev probing phase. Documentation for these parameters is also
updated.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/net/sxe2/sxe2_cmd_chnl.c |  21 +++
 drivers/net/sxe2/sxe2_cmd_chnl.h |   3 +
 drivers/net/sxe2/sxe2_drv_cmd.h  |  17 ++
 drivers/net/sxe2/sxe2_dump.c     |  15 ++
 drivers/net/sxe2/sxe2_ethdev.c   | 272 +++++++++++++++++++++++++++++--
 drivers/net/sxe2/sxe2_ethdev.h   |   6 +
 drivers/net/sxe2/sxe2_flow.c     |   9 +-
 drivers/net/sxe2/sxe2_irq.c      |  30 ++++
 drivers/net/sxe2/sxe2_rx.c       |  12 ++
 9 files changed, 369 insertions(+), 16 deletions(-)

diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.c b/drivers/net/sxe2/sxe2_cmd_chnl.c
index 43e8c59487..b09989fe50 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.c
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.c
@@ -99,6 +99,27 @@ int32_t sxe2_drv_dev_info_get(struct sxe2_adapter *adapter,
 	return ret;
 }
 
+int32_t sxe2_drv_fc_state_get(struct sxe2_adapter *adapter,
+			      struct sxe2_drv_vsi_fc_get_resp *dev_fc_state_resp)
+{
+	int32_t ret = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_drv_vsi_fc_get_req req = {0};
+
+	req.vsi_id = adapter->vsi_ctxt.main_vsi->vsi_id;
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_VSI_FC_GET,
+				&req, sizeof(req),
+				dev_fc_state_resp,
+				sizeof(*dev_fc_state_resp));
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "get fc state failed, ret=%d", ret);
+		ret = -EIO;
+	}
+	return ret;
+}
+
 int32_t sxe2_drv_dev_fw_info_get(struct sxe2_adapter *adapter,
 				struct sxe2_drv_dev_fw_info_resp *dev_fw_info_resp)
 {
diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.h b/drivers/net/sxe2/sxe2_cmd_chnl.h
index 988d4b458b..d63caad526 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.h
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.h
@@ -99,6 +99,9 @@ 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_fc_state_get(struct sxe2_adapter *adapter,
+			      struct sxe2_drv_vsi_fc_get_resp *dev_fc_state_resp);
+
 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);
diff --git a/drivers/net/sxe2/sxe2_drv_cmd.h b/drivers/net/sxe2/sxe2_drv_cmd.h
index 3fabf351af..03ef3b315d 100644
--- a/drivers/net/sxe2/sxe2_drv_cmd.h
+++ b/drivers/net/sxe2/sxe2_drv_cmd.h
@@ -651,6 +651,23 @@ struct __rte_aligned(4) __rte_packed_begin sxe2_drv_sfp_resp {
 	uint8_t data[];
 } __rte_packed_end;
 
+enum sxe2_fc_type {
+	SXE2_FC_T_DIS = 0,
+	SXE2_FC_T_LFC,
+	SXE2_FC_T_PFC,
+	SXE2_FC_T_UNKNOWN = 255,
+};
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_vsi_fc_get_req {
+	uint16_t vsi_id;
+	uint8_t rsv[2];
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_vsi_fc_get_resp {
+	uint8_t fc_enable;
+	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_dump.c b/drivers/net/sxe2/sxe2_dump.c
index d43473e083..c7cdde5f4f 100644
--- a/drivers/net/sxe2/sxe2_dump.c
+++ b/drivers/net/sxe2/sxe2_dump.c
@@ -186,6 +186,20 @@ static void sxe2_dump_filter_info(FILE *file, struct rte_eth_dev *dev)
 	return;
 }
 
+static void sxe2_dump_fc_state(FILE *file, struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	if (!(adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_FC_STATE))
+		goto l_end;
+
+	fprintf(file, " -- fc state:\n"
+		"\t  -- curr_state: %u\n",
+		adapter->fc_state_ctx.curr_state);
+l_end:
+	return;
+}
+
 static const char *sxe2_vsi_id_str(uint16_t vsi_id, char *buf, size_t len)
 {
 	if (vsi_id == SXE2_INVALID_VSI_ID)
@@ -272,6 +286,7 @@ int32_t sxe2_eth_dev_priv_dump(struct rte_eth_dev *dev, FILE *file)
 	sxe2_dump_dev_args_info(str, dev);
 	sxe2_dump_filter_info(str, dev);
 	sxe2_dump_switchdev_info(str, dev);
+	sxe2_dump_fc_state(str, dev);
 
 	(void)fflush(str);
 
diff --git a/drivers/net/sxe2/sxe2_ethdev.c b/drivers/net/sxe2/sxe2_ethdev.c
index c140c26be1..ef68a28ed1 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -69,6 +69,15 @@ static const struct rte_pci_id pci_id_sxe2_tbl[] = {
 	{ RTE_PCI_DEVICE(SXE2_PCI_VENDOR_ID_206F, SXE2_PCI_DEVICE_ID_VF_1)},
 	{ .vendor_id = 0, },
 };
+#define SXE2_TXSCH_NODE_ADJ_LVL_MAX  3
+#define SXE2_DEVARG_FLOW_DULP_PATTERN_MODE "flow-duplicate-pattern"
+#define SXE2_DEVARG_FUNC_FLOW_DIRCT "function-flow-direct"
+#define SXE2_DEVARG_FNAV_STAT_TYPE "fnav-stat-type"
+#define SXE2_DEVARG_NO_SCHED_MODE "no-sched-mode"
+#define SXE2_DEVARG_SCHED_LAYER_MODE "sched-layer-mode"
+#define SXE2_DEVARG_RX_LOW_LATENCY "rx-low-latency"
+
+#define SXE2_FLOW_DUP_PATTERN_DEFAULT  1
 
 static struct sxe2_pci_map_addr_info sxe2_net_map_addr_info_pf[SXE2_PCI_MAP_RES_MAX_COUNT] = {
 	[SXE2_PCI_MAP_RES_INVALID] = {.addr_base = 0,
@@ -965,6 +974,149 @@ sxe2_buffer_split_supported_hdr_ptypes_get(struct rte_eth_dev *dev __rte_unused,
 	return ptypes;
 }
 
+static int32_t sxe2_parse_fnav_stat_type(const char *key, const char *value, void *args)
+{
+	int32_t ret = -EINVAL;
+	uint8_t *num = (uint8_t *)args;
+	unsigned long fnav_stat_type;
+	char *endptr = NULL;
+
+	if (value == NULL || args == NULL) {
+		ret = 0;
+		goto l_end;
+	}
+	errno = 0;
+	fnav_stat_type = strtoul(value, &endptr, 10);
+	if (errno != 0 || endptr == value || *endptr != '\0') {
+		PMD_LOG_WARN(INIT, "%s: \"%s\" is not a valid int value.",
+			key, value);
+		goto l_end;
+	}
+	if (fnav_stat_type > SXE2_FNAV_STAT_ENA_ALL ||
+		fnav_stat_type == SXE2_FNAV_STAT_ENA_NONE) {
+		PMD_LOG_ERR(INIT, "%s: \"%s\" out of range [1-3].",
+			key, value);
+		goto l_end;
+	}
+	*num = (uint8_t)fnav_stat_type;
+	ret = 0;
+l_end:
+	return ret;
+}
+static int32_t sxe2_parse_sched_layer_mode(const char *key, const char *value, void *args)
+{
+	int32_t ret = -EINVAL;
+	uint8_t *num = (uint8_t *)args;
+	unsigned long sched_layer_mode;
+	char *endptr = NULL;
+
+	if (value == NULL || args == NULL) {
+		ret = 0;
+		goto l_end;
+	}
+	errno = 0;
+	sched_layer_mode = strtoul(value, &endptr, 10);
+	if (errno != 0 || endptr == value || *endptr != '\0') {
+		PMD_LOG_WARN(INIT, "%s: \"%s\" is not a valid int value.",
+			key, value);
+		goto l_end;
+	}
+	if (sched_layer_mode > SXE2_TXSCH_NODE_ADJ_LVL_MAX) {
+		PMD_LOG_ERR(INIT, "%s: \"%s\" > 3.",
+			key, value);
+		goto l_end;
+	}
+	*num = (uint8_t)sched_layer_mode;
+	ret = 0;
+l_end:
+	return ret;
+}
+static int32_t sxe2_parse_no_sched_mode(const char *key, const char *value, void *args)
+{
+	int32_t ret = -EINVAL;
+	uint8_t *num = (uint8_t *)args;
+	unsigned long no_sched_mode;
+	char *endptr = NULL;
+
+	if (value == NULL || args == NULL) {
+		ret = 0;
+		goto l_end;
+	}
+	errno = 0;
+	no_sched_mode = strtoul(value, &endptr, 10);
+	if (errno != 0 || endptr == value || *endptr != '\0') {
+		PMD_LOG_WARN(INIT, "%s: \"%s\" is not a valid int value.",
+			key, value);
+		goto l_end;
+	}
+	if (no_sched_mode != 1) {
+		PMD_LOG_ERR(INIT, "%s: \"%s\" != 1.",
+			key, value);
+		goto l_end;
+	}
+	*num = (uint8_t)no_sched_mode;
+	ret = 0;
+l_end:
+	return ret;
+}
+static int32_t sxe2_parse_u8(const char *key, const char *value, void *args)
+{
+	uint8_t *num = (uint8_t *)args;
+	char *end;
+	unsigned long val;
+	int32_t ret = -EINVAL;
+
+	if (value == NULL || args == NULL) {
+		ret = 0;
+		goto l_end;
+	}
+	errno = 0;
+	val = strtoul(value, &end, 10);
+	if (errno != 0 || end == value || *end != '\0') {
+		PMD_LOG_ERR(INIT, "Invalid 8-bit integer value for key %s: %s", key, value);
+		return -EINVAL;
+	}
+
+	if (val > UINT8_MAX) {
+		PMD_LOG_ERR(INIT, "%s: \"%s\" out of range [0-255].",
+			key, value);
+		return -ERANGE;
+	}
+
+	*num = (uint8_t)val;
+	ret = 0;
+l_end:
+	return ret;
+}
+static int32_t sxe2_parse_bool(const char *key, const char *value, void *args)
+{
+	int32_t ret = -EINVAL;
+	uint8_t *num = (uint8_t *)args;
+	unsigned long bool_val;
+	char *endptr = NULL;
+
+	if (value == NULL || args == NULL) {
+		ret = 0;
+		goto l_end;
+	}
+	errno = 0;
+	bool_val = strtoul(value, &endptr, 10);
+	if (errno != 0 || endptr == value || *endptr != '\0') {
+		PMD_LOG_WARN(INIT, "%s: \"%s\" is not a valid int value.",
+			key, value);
+		goto l_end;
+	}
+	if (bool_val != 0 && bool_val != 1) {
+		PMD_LOG_ERR(INIT, "%s: \"%s\" out of range [0|1].",
+			key, value);
+		goto l_end;
+	}
+	*num = (uint8_t)bool_val;
+	ret = 0;
+l_end:
+	return ret;
+}
+
 struct sxe2_pci_map_bar_info *sxe2_dev_get_bar_info(struct sxe2_adapter *adapter,
 						    enum sxe2_pci_map_resource res_type)
 {
@@ -1032,6 +1184,65 @@ void *sxe2_pci_map_addr_get(struct sxe2_adapter *adapter,
 	return addr;
 }
 
+static int32_t sxe2_args_parse(struct rte_eth_dev *dev, struct sxe2_dev_kvargs_info *kvargs)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+	PMD_INIT_FUNC_TRACE();
+
+	adapter->devargs.flow_dup_pattern_mode = SXE2_FLOW_DUP_PATTERN_DEFAULT;
+
+	if (kvargs == NULL)
+		goto l_end;
+	ret = sxe2_kvargs_process(kvargs, SXE2_DEVARG_FNAV_STAT_TYPE,
+				 &sxe2_parse_fnav_stat_type,
+				 &adapter->devargs.fnav_stat_type);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to parse fnav stat type, ret:%d", ret);
+		goto l_end;
+	}
+
+	ret = sxe2_kvargs_process(kvargs, SXE2_DEVARG_NO_SCHED_MODE,
+				 &sxe2_parse_no_sched_mode,
+				 &adapter->devargs.no_sched_mode);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to parse no sched mode, ret:%d", ret);
+		goto l_end;
+	}
+	ret = sxe2_kvargs_process(kvargs, SXE2_DEVARG_SCHED_LAYER_MODE,
+				 &sxe2_parse_sched_layer_mode,
+				 &adapter->devargs.sched_layer_mode);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to parse sched layer mode, ret:%d", ret);
+		goto l_end;
+	}
+	ret = sxe2_kvargs_process(kvargs, SXE2_DEVARG_FLOW_DULP_PATTERN_MODE,
+				 &sxe2_parse_u8,
+				 &adapter->devargs.flow_dup_pattern_mode);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to parse switch dulpliate flow pattern mode,"
+				"ret:%d", ret);
+		goto l_end;
+	}
+	ret = sxe2_kvargs_process(kvargs, SXE2_DEVARG_FUNC_FLOW_DIRCT,
+				 &sxe2_parse_bool,
+				 &adapter->devargs.func_flow_direct_en);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to parse function flow rule enable,"
+				"ret:%d", ret);
+		goto l_end;
+	}
+	ret = sxe2_kvargs_process(kvargs, SXE2_DEVARG_RX_LOW_LATENCY,
+				 &sxe2_parse_bool,
+				 &adapter->devargs.rx_low_latency);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to parse rx low latency, ret:%d", ret);
+		goto l_end;
+	}
+l_end:
+	return ret;
+}
+
 static int32_t sxe2_eth_init(struct rte_eth_dev *dev)
 {
 	int32_t ret = 0;
@@ -1584,6 +1795,37 @@ void sxe2_dev_pci_map_uinit(struct rte_eth_dev *dev)
 	adapter->dev_info.dev_data = NULL;
 }
 
+static int32_t sxe2_fc_state_init(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_drv_vsi_fc_get_resp fc_resp = {0};
+	int32_t ret;
+
+	if (!(adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_FC_STATE)) {
+		adapter->fc_state_ctx.cfg_state = 0;
+		adapter->fc_state_ctx.curr_state = 0;
+		ret = 0;
+		goto l_end;
+	}
+	ret = sxe2_drv_fc_state_get(adapter, &fc_resp);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to get fc state, ret=[%d]", ret);
+		goto l_end;
+	}
+	adapter->fc_state_ctx.cfg_state = fc_resp.fc_enable;
+	adapter->fc_state_ctx.curr_state = 0;
+l_end:
+	return ret;
+}
+static void sxe2_fc_state_uinit(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	adapter->fc_state_ctx.cfg_state = 0;
+	adapter->fc_state_ctx.curr_state = 0;
+}
+
 uint32_t sxe2_sched_mode_get(struct sxe2_adapter *adapter)
 {
 	uint32_t ret_mode = SXE2_SCHED_MODE_INVALID;
@@ -1646,18 +1888,6 @@ static int32_t sxe2_sched_uinit(struct rte_eth_dev *dev)
 	return ret;
 }
 
-static int32_t sxe2_args_parse(struct rte_eth_dev *dev,
-			       __rte_unused struct sxe2_dev_kvargs_info *kvargs)
-{
-	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
-	int32_t ret = 0;
-	PMD_INIT_FUNC_TRACE();
-
-	adapter->devargs.flow_dup_pattern_mode = SXE2_FLOW_DUP_PATTERN_DEFAULT;
-
-	return ret;
-}
-
 static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 			     struct sxe2_dev_kvargs_info *kvargs)
 {
@@ -1680,7 +1910,7 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 
 	ret = sxe2_args_parse(dev, kvargs);
 	if (ret) {
-		PMD_LOG_ERR(INIT, "Failed to parse args, ret=[%d]", ret);
+		PMD_LOG_ERR(INIT, "Failed to parse devargs, ret=%d", ret);
 		goto l_end;
 	}
 
@@ -1750,6 +1980,12 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 		goto init_flow_err;
 	}
 
+	ret = sxe2_fc_state_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to init fc state, ret=%d", ret);
+		goto init_fc_state_err;
+	}
+
 	ret = sxe2_sched_init(dev);
 	if (ret) {
 		PMD_LOG_ERR(INIT, "Failed to init sched, ret=%d", ret);
@@ -1773,6 +2009,8 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 init_xstats_err:
 	(void)sxe2_sched_uinit(dev);
 init_sched_err:
+	sxe2_fc_state_uinit(dev);
+init_fc_state_err:
 	(void)sxe2_flow_uninit(dev);
 init_flow_err:
 init_rss_err:
@@ -1818,6 +2056,7 @@ static int32_t sxe2_dev_close(struct rte_eth_dev *dev)
 	sxe2_eth_uinit(dev);
 	sxe2_dev_pci_map_uinit(dev);
 	sxe2_free_repr_info(dev);
+	sxe2_fc_state_uinit(dev);
 
 l_end:
 	return 0;
@@ -2123,6 +2362,13 @@ RTE_INIT(rte_sxe2_pmd_init)
 RTE_PMD_EXPORT_NAME(net_sxe2);
 RTE_PMD_REGISTER_PCI_TABLE(net_sxe2, pci_id_sxe2_tbl);
 RTE_PMD_REGISTER_KMOD_DEP(net_sxe2, "* sxe2");
+RTE_PMD_REGISTER_PARAM_STRING(net_sxe2,
+	"flow-duplicate-pattern=<0|1> "
+	"function-flow-direct=<0|1> "
+	"fnav-stat-type=<1|2|3> "
+	"no-sched-mode=<0|1> "
+	"sched-layer-mode=<0-3> "
+	"rx-low-latency=<0|1>");
 
 RTE_LOG_REGISTER_SUFFIX(sxe2_log_init, init, NOTICE);
 RTE_LOG_REGISTER_SUFFIX(sxe2_log_driver, driver, NOTICE);
diff --git a/drivers/net/sxe2/sxe2_ethdev.h b/drivers/net/sxe2/sxe2_ethdev.h
index a68b95c0d0..c54e8a435e 100644
--- a/drivers/net/sxe2/sxe2_ethdev.h
+++ b/drivers/net/sxe2/sxe2_ethdev.h
@@ -310,6 +310,11 @@ struct sxe2_filter_context {
 	bool cur_l2_config;
 };
 
+struct sxe2_fc_state_ctxt {
+	uint8_t curr_state;
+	uint8_t cfg_state;
+};
+
 struct sxe2_adapter {
 	struct sxe2_common_device      *cdev;
 	struct sxe2_dev_info            dev_info;
@@ -331,6 +336,7 @@ struct sxe2_adapter {
 	struct sxe2_security_ctx      security_ctx;
 	struct sxe2_repr_context      repr_ctxt;
 	struct sxe2_switchdev_info    switchdev_info;
+	struct sxe2_fc_state_ctxt     fc_state_ctx;
 	bool                          rule_started;
 	bool                          flow_isolated;
 	bool                          flow_isolate_cfg;
diff --git a/drivers/net/sxe2/sxe2_flow.c b/drivers/net/sxe2/sxe2_flow.c
index 63cfc36968..1aa5813ee4 100644
--- a/drivers/net/sxe2/sxe2_flow.c
+++ b/drivers/net/sxe2/sxe2_flow.c
@@ -762,6 +762,7 @@ static int32_t sxe2_flow_validate_with_flow(struct rte_eth_dev *dev,
 					const struct rte_flow_action actions[],
 					struct rte_flow_error *error)
 {
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
 	int32_t ret = 0;
 	struct sxe2_flow *flow = NULL;
 
@@ -804,9 +805,11 @@ static int32_t sxe2_flow_validate_with_flow(struct rte_eth_dev *dev,
 
 	ret = sxe2_flow_check_flow_list_duplicate(dev, flow_list);
 	if (ret != 0) {
-		rte_flow_error_set(error, EEXIST, RTE_FLOW_ERROR_TYPE_ITEM,
-				NULL, "Duplicate flow.");
-		PMD_LOG_ERR(DRV, "Duplicate flow.");
+		rte_flow_error_set(error, EEXIST, RTE_FLOW_ERROR_TYPE_ITEM, NULL,
+				   adapter->devargs.flow_dup_pattern_mode ?
+				   "Duplicate flow pattern." :
+				   "Duplicate flow pattern is not allowed.");
+		PMD_LOG_ERR(DRV, "Duplicate flow pattern.");
 		goto l_end;
 	}
 l_end:
diff --git a/drivers/net/sxe2/sxe2_irq.c b/drivers/net/sxe2/sxe2_irq.c
index d8e0b19463..3306504761 100644
--- a/drivers/net/sxe2/sxe2_irq.c
+++ b/drivers/net/sxe2/sxe2_irq.c
@@ -10,6 +10,7 @@
 #include <rte_alarm.h>
 #include <fcntl.h>
 #include <rte_stdatomic.h>
+#include <rte_common.h>
 
 #include "sxe2_ethdev.h"
 #include "sxe2_irq.h"
@@ -47,6 +48,31 @@ static struct sxe2_event_handler event_handler = {
 
 static RTE_ATOMIC(uint32_t)event_thread_run;
 
+static int32_t sxe2_fc_state_callback(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_drv_vsi_fc_get_resp fc_resp = {0};
+	int32_t ret;
+
+	if (!(adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_FC_STATE)) {
+		ret = 0;
+		goto l_end;
+	}
+	ret = sxe2_drv_fc_state_get(adapter, &fc_resp);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to get fc state, ret=[%d]", ret);
+		goto l_end;
+	}
+	adapter->fc_state_ctx.cfg_state = fc_resp.fc_enable;
+	if (dev->data->dev_started) {
+		PMD_LOG_NOTICE(DRV, "Interrupt event: FC status changed."
+			       "cfg_state:%u curr_state:%u",
+				adapter->fc_state_ctx.cfg_state,
+				adapter->fc_state_ctx.curr_state);
+	}
+l_end:
+	return ret;
+}
 
 static void sxe2_event_irq_common_handler(struct sxe2_adapter *adapter, uint64_t oicr)
 {
@@ -68,6 +94,10 @@ static void sxe2_event_irq_common_handler(struct sxe2_adapter *adapter, uint64_t
 		PMD_DEV_LOG_INFO(adapter, DRV, "event notify legacy");
 		(void)sxe2_switchdev_notify_callback(adapter, false);
 	}
+	if (oicr & RTE_BIT32(SXE2_COM_FC_ST_CHANGE)) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "fc event notify legacy");
+		(void)sxe2_fc_state_callback(dev);
+	}
 }
 
 static uint32_t sxe2_event_intr_handle(void *param __rte_unused)
diff --git a/drivers/net/sxe2/sxe2_rx.c b/drivers/net/sxe2/sxe2_rx.c
index 820d4f0620..d700c60083 100644
--- a/drivers/net/sxe2/sxe2_rx.c
+++ b/drivers/net/sxe2/sxe2_rx.c
@@ -467,12 +467,24 @@ int32_t __rte_cold sxe2_rx_queue_start(struct rte_eth_dev *dev, uint16_t rx_queu
 int32_t __rte_cold sxe2_rxqs_all_start(struct rte_eth_dev *dev)
 {
 	struct rte_eth_dev_data *data = dev->data;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_drv_vsi_fc_get_resp fc_resp = {0};
 	struct sxe2_rx_queue *rxq;
 	uint16_t nb_rxq;
 	uint16_t nb_started_rxq;
 	int32_t ret;
 	PMD_INIT_FUNC_TRACE();
 
+	if (adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_FC_STATE) {
+		ret = sxe2_drv_fc_state_get(adapter, &fc_resp);
+		if (ret) {
+			PMD_LOG_ERR(RX, "Failed to get fc state, ret=[%d]", ret);
+			goto l_end;
+		}
+		adapter->fc_state_ctx.cfg_state = fc_resp.fc_enable;
+		adapter->fc_state_ctx.curr_state = adapter->fc_state_ctx.cfg_state;
+	}
+
 	for (nb_rxq = 0; nb_rxq < data->nb_rx_queues; nb_rxq++) {
 		rxq = dev->data->rx_queues[nb_rxq];
 		if (!rxq || rxq->rx_deferred_start)
-- 
2.52.0


^ permalink raw reply related

* [PATCH v5 21/23] common/sxe2: add callback for memory event handling
From: liujie5 @ 2026-06-22  9:27 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260622092324.3091185-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

During memory hotplug events, the SXE2 driver needs to track memory
segment layout changes to maintain internal DMA mappings. However,
existing memseg walk functions (rte_memseg_walk) acquire memory locks
and cannot be called from within memory event callbacks, leading to
potential deadlocks.

The implementation follows the standard rte_memseg_walk_t prototype,
processing each memseg to update driver-specific data structures.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/common/sxe2/sxe2_common.c     | 110 ++++++++++++++++++++++++++
 drivers/common/sxe2/sxe2_common.h     |   2 +
 drivers/common/sxe2/sxe2_ioctl_chnl.c |   2 +-
 3 files changed, 113 insertions(+), 1 deletion(-)

diff --git a/drivers/common/sxe2/sxe2_common.c b/drivers/common/sxe2/sxe2_common.c
index c000a55cd0..5c5db85f29 100644
--- a/drivers/common/sxe2/sxe2_common.c
+++ b/drivers/common/sxe2/sxe2_common.c
@@ -196,6 +196,102 @@ static int32_t sxe2_parse_representor(const char *key, const char *value, void *
 
 	PMD_LOG_INFO(COM, "representor arg %s: \"%s\".", key, value);
 
+l_end:
+	return ret;
+}
+static int32_t sxe2_dma_mem_map(struct sxe2_common_device *cdev,
+				const void *addr, size_t len, bool do_map)
+{
+	struct rte_memseg_list *msl;
+	struct rte_memseg *ms;
+	size_t cur_len = 0;
+	int32_t ret = 0;
+
+	msl = rte_mem_virt2memseg_list(addr);
+	if (msl == NULL) {
+		ret = -EINVAL;
+		PMD_LOG_ERR(COM, "Invalid virt addr=%p.", addr);
+		goto l_end;
+	}
+
+	if ((uintptr_t)addr != RTE_ALIGN((uintptr_t)addr, msl->page_sz) ||
+		(len != RTE_ALIGN(len, msl->page_sz))) {
+		ret = -EINVAL;
+		PMD_LOG_ERR(COM, "Addr=%p and len=%zu not align page size=%" PRIu64 ".",
+			    addr, len, msl->page_sz);
+		goto l_end;
+	}
+
+	/* memsegs are contiguous in memory */
+	ms = rte_mem_virt2memseg(addr, msl);
+	while (cur_len < len) {
+		/* some memory segments may have invalid IOVA */
+		if (ms->iova == RTE_BAD_IOVA) {
+			PMD_LOG_WARN(COM, "Memory segment at %p has bad IOVA, skipping.",
+					ms->addr);
+			goto next;
+		}
+		if (do_map)
+			sxe2_drv_dev_dma_map(cdev, ms->addr_64,
+					ms->iova, ms->len);
+		else
+			sxe2_drv_dev_dma_unmap(cdev, ms->iova);
+
+next:
+		cur_len += ms->len;
+		++ms;
+	}
+
+l_end:
+	return ret;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(sxe2_common_mem_event_cb)
+void
+sxe2_common_mem_event_cb(enum rte_mem_event type,
+		const void *addr, size_t size, void *arg __rte_unused)
+{
+	struct sxe2_common_device *cdev = NULL;
+
+	if (rte_eal_process_type() != RTE_PROC_PRIMARY)
+		goto l_end;
+
+	pthread_mutex_lock(&sxe2_common_devices_list_lock);
+	switch (type) {
+	case RTE_MEM_EVENT_FREE:
+		TAILQ_FOREACH(cdev, &sxe2_common_devices_list, next)
+			(void)sxe2_dma_mem_map(cdev, addr, size, 0);
+		break;
+	case RTE_MEM_EVENT_ALLOC:
+		TAILQ_FOREACH(cdev, &sxe2_common_devices_list, next)
+			(void)sxe2_dma_mem_map(cdev, addr, size, 1);
+		break;
+	default:
+		break;
+	}
+	pthread_mutex_unlock(&sxe2_common_devices_list_lock);
+l_end:
+	return;
+}
+
+static int32_t sxe2_memseg_walk_cb(const struct rte_memseg_list *msl,
+				   const struct rte_memseg *ms, void *arg)
+{
+	struct sxe2_common_device *cdev = arg;
+	int32_t ret = 0;
+
+	if (msl->external && !msl->heap)
+		goto l_end;
+
+	if (ms->iova == RTE_BAD_IOVA)
+		goto l_end;
+
+	ret = sxe2_drv_dev_dma_map(cdev, ms->addr_64, ms->iova, ms->len);
+	if (ret != 0) {
+		PMD_LOG_ERR(COM, "Fail to memseg dma map.");
+		goto l_end;
+	}
+
 l_end:
 	return ret;
 }
@@ -220,6 +316,18 @@ static int32_t sxe2_common_device_setup(struct sxe2_common_device *cdev)
 		goto l_close_dev;
 	}
 
+	rte_mcfg_mem_read_lock();
+	ret = rte_memseg_walk_thread_unsafe(sxe2_memseg_walk_cb, cdev);
+	if (ret) {
+		PMD_LOG_ERR(COM, "Fail to walk memseg, ret=%d", ret);
+		rte_mcfg_mem_read_unlock();
+		goto l_close_dev;
+	}
+	rte_mcfg_mem_read_unlock();
+
+	(void)rte_mem_event_callback_register("SXE2_MEM_EVENT_CB",
+			sxe2_common_mem_event_cb, NULL);
+
 	goto l_end;
 
 l_close_dev:
@@ -251,6 +359,7 @@ static struct sxe2_common_device *sxe2_common_device_alloc(
 	}
 	cdev->dev = rte_dev;
 	cdev->class_type = class_type;
+	cdev->config.cmd_fd = SXE2_CMD_FD_INVALID;
 	cdev->config.kernel_reset = false;
 	pthread_mutex_init(&cdev->config.lock, NULL);
 
@@ -631,6 +740,7 @@ static int32_t sxe2_common_pci_id_table_update(const struct rte_pci_id *id_table
 
 	updated_table = calloc(num_ids, sizeof(*updated_table));
 	if (!updated_table) {
+		ret = -ENOMEM;
 		PMD_LOG_ERR(COM, "Failed to allocate memory for PCI ID table");
 		goto l_end;
 	}
diff --git a/drivers/common/sxe2/sxe2_common.h b/drivers/common/sxe2/sxe2_common.h
index b02b6317da..efc8d3585a 100644
--- a/drivers/common/sxe2/sxe2_common.h
+++ b/drivers/common/sxe2/sxe2_common.h
@@ -14,6 +14,8 @@
 
 #define SXE2_COMMON_PCI_DRIVER_NAME "sxe2_pci"
 
+#define SXE2_CMD_FD_INVALID (-1)
+
 #define SXE2_CDEV_TO_CMD_FD(cdev) \
 	((cdev)->config.cmd_fd)
 
diff --git a/drivers/common/sxe2/sxe2_ioctl_chnl.c b/drivers/common/sxe2/sxe2_ioctl_chnl.c
index 173d8d57ae..a233a78136 100644
--- a/drivers/common/sxe2/sxe2_ioctl_chnl.c
+++ b/drivers/common/sxe2/sxe2_ioctl_chnl.c
@@ -110,7 +110,7 @@ sxe2_drv_dev_close(struct sxe2_common_device *cdev)
 	if (fd >= 0)
 		close(fd);
 	PMD_LOG_INFO(COM, "closed device fd=%d", fd);
-	SXE2_CDEV_TO_CMD_FD(cdev) = -1;
+	SXE2_CDEV_TO_CMD_FD(cdev) = SXE2_CMD_FD_INVALID;
 }
 
 RTE_EXPORT_INTERNAL_SYMBOL(sxe2_drv_dev_handshake)
-- 
2.52.0


^ permalink raw reply related

* [PATCH v5 20/23] net/sxe2: add mbuf validation in Tx debug mode
From: liujie5 @ 2026-06-22  9:27 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260622092324.3091185-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

Introduce the `sxe2_txrx_check_mbuf` helper function to validate outgoing
mbufs when `RTE_ETHDEV_DEBUG_TX` is enabled. This helps developers catch
malformed mbufs (e.g., invalid segment lengths, bad offload flags, or
unaligned buffers) before passing them to the hardware rings, avoiding
potential hardware hangs or silent packet drops.

The validation is fully wrapped inside `RTE_ETHDEV_DEBUG_TX` conditional
compilation blocks to ensure zero performance overhead in standard
production builds.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/net/sxe2/meson.build            |   1 +
 drivers/net/sxe2/sxe2_txrx.c            |   8 +-
 drivers/net/sxe2/sxe2_txrx_check_mbuf.c | 595 ++++++++++++++++++++++++
 drivers/net/sxe2/sxe2_txrx_check_mbuf.h |  38 ++
 4 files changed, 640 insertions(+), 2 deletions(-)
 create mode 100644 drivers/net/sxe2/sxe2_txrx_check_mbuf.c
 create mode 100644 drivers/net/sxe2/sxe2_txrx_check_mbuf.h

diff --git a/drivers/net/sxe2/meson.build b/drivers/net/sxe2/meson.build
index d653d071a9..2405213809 100644
--- a/drivers/net/sxe2/meson.build
+++ b/drivers/net/sxe2/meson.build
@@ -78,4 +78,5 @@ sources += files(
         'sxe2_flow_parse_pattern.c',
         'sxe2_flow_parse_engine.c',
         'sxe2_dump.c',
+        'sxe2_txrx_check_mbuf.c',
 )
diff --git a/drivers/net/sxe2/sxe2_txrx.c b/drivers/net/sxe2/sxe2_txrx.c
index 82b2e4fb7c..2081b96499 100644
--- a/drivers/net/sxe2/sxe2_txrx.c
+++ b/drivers/net/sxe2/sxe2_txrx.c
@@ -13,6 +13,7 @@
 #include "sxe2_txrx_common.h"
 #include "sxe2_txrx_vec.h"
 #include "sxe2_txrx_poll.h"
+#include "sxe2_txrx_check_mbuf.h"
 #include "sxe2_ethdev.h"
 #include "sxe2_common_log.h"
 #include "sxe2_osal.h"
@@ -120,13 +121,11 @@ uint16_t sxe2_tx_pkts_prepare(void *tx_queue,
 			rte_errno = -EINVAL;
 			goto l_end;
 		}
-#ifdef RTE_ETHDEV_DEBUG_TX
 		ret = rte_validate_tx_offload(mbuf);
 		if (ret != 0) {
 			rte_errno = -ret;
 			goto l_end;
 		}
-#endif
 		ret = rte_net_intel_cksum_prepare(mbuf);
 		if (ret != 0) {
 			rte_errno = -ret;
@@ -137,6 +136,11 @@ uint16_t sxe2_tx_pkts_prepare(void *tx_queue,
 			rte_errno = -ret;
 			goto l_end;
 		}
+		ret = sxe2_txrx_check_mbuf(mbuf);
+		if (ret != 0) {
+			rte_errno = -ret;
+			goto l_end;
+		}
 	}
 l_end:
 	return i;
diff --git a/drivers/net/sxe2/sxe2_txrx_check_mbuf.c b/drivers/net/sxe2/sxe2_txrx_check_mbuf.c
new file mode 100644
index 0000000000..7d316ae652
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_txrx_check_mbuf.c
@@ -0,0 +1,595 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#include <rte_common.h>
+#include <rte_net.h>
+#include <rte_vect.h>
+#include <rte_malloc.h>
+#include <rte_memzone.h>
+#include <ethdev_driver.h>
+#include <rte_geneve.h>
+
+#include "sxe2_txrx_check_mbuf.h"
+#include "sxe2_common_log.h"
+
+#define TX_IPPROTO_IPIP 4
+#define TX_IPPROTO_GRE  47
+#define GRE_CHECKSUM_PRESENT 0x8000
+#define GRE_KEY_PRESENT 0x2000
+#define GRE_SEQUENCE_PRESENT 0x1000
+#define GRE_EXT_LEN 4
+#define GRE_SUPPORTED_FIELDS (GRE_CHECKSUM_PRESENT | GRE_KEY_PRESENT | GRE_SEQUENCE_PRESENT)
+
+
+static uint16_t vxlan_gpe_udp_port = RTE_VXLAN_GPE_DEFAULT_PORT;
+static uint16_t geneve_udp_port = RTE_GENEVE_DEFAULT_PORT;
+
+static inline int32_t check_mbuf_len(struct offload_info *info, struct rte_mbuf *m)
+{
+	int32_t ret = 0;
+	if (m->ol_flags & RTE_MBUF_F_TX_TUNNEL_MASK) {
+		if (info->outer_l2_len != m->outer_l2_len) {
+			PMD_LOG_ERR(TX, "outer_l2_len error in mbuf. Original "
+				    "length:%u calculated length:%u", m->outer_l2_len,
+				    info->outer_l2_len);
+			ret = -1;
+			goto end;
+		}
+		if (info->outer_l3_len != m->outer_l3_len) {
+			PMD_LOG_ERR(TX, "outer_l3_len error in mbuf. Original "
+				    "length:%u calculated length:%u", m->outer_l3_len,
+				    info->outer_l3_len);
+			ret = -1;
+			goto end;
+		}
+	}
+
+	if (info->l2_len != m->l2_len) {
+		PMD_LOG_ERR(TX, "l2_len error in mbuf. Original "
+			"length:%u calculated length:%u", m->l2_len, info->l2_len);
+		ret = -1;
+		goto end;
+	}
+	if (info->l3_len != m->l3_len) {
+		PMD_LOG_ERR(TX, "l3_len error in mbuf. Original "
+			"length:%u calculated length:%u", m->l3_len, info->l3_len);
+		ret = -1;
+		goto end;
+	}
+	if (info->l4_len != m->l4_len) {
+		PMD_LOG_ERR(TX, "l4_len error in mbuf. Original "
+			"length:%u calculated length:%u", m->l4_len, info->l4_len);
+		ret = -1;
+		goto end;
+	}
+	ret = 0;
+
+end:
+	return ret;
+}
+
+static inline int32_t check_ether_type(struct offload_info *info, struct rte_mbuf *m)
+{
+	int32_t ret = 0;
+
+	if (m->ol_flags & RTE_MBUF_F_TX_TUNNEL_MASK) {
+		if (info->outer_ethertype == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)) {
+			if (!(m->ol_flags & RTE_MBUF_F_TX_OUTER_IPV4)) {
+				PMD_LOG_ERR(TX, "Outer ethernet type is ipv4, "
+					"tx offload missing `RTE_MBUF_F_TX_OUTER_IPV4` flag");
+				ret = -1;
+				goto end;
+			}
+			if (m->ol_flags & RTE_MBUF_F_TX_OUTER_IPV6) {
+				PMD_LOG_ERR(TX, "Outer ethernet type is ipv4, tx "
+					"offload contains wrong `RTE_MBUF_F_TX_OUTER_IPV6` flag");
+				ret = -1;
+				goto end;
+			}
+		} else if (info->outer_ethertype == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6)) {
+			if (!(m->ol_flags & RTE_MBUF_F_TX_OUTER_IPV6)) {
+				PMD_LOG_ERR(TX, "Outer ethernet type is ipv6, "
+					"tx offload missing `RTE_MBUF_F_TX_OUTER_IPV6` flag");
+				ret = -1;
+				goto end;
+			}
+			if (m->ol_flags & RTE_MBUF_F_TX_OUTER_IPV4) {
+				PMD_LOG_ERR(TX, "Outer ethernet type is ipv6, tx "
+					"offload contains wrong `RTE_MBUF_F_TX_OUTER_IPV4` flag");
+				ret = -1;
+				goto end;
+			}
+		}
+	}
+
+	if (info->ethertype == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)) {
+		if (!(m->ol_flags & RTE_MBUF_F_TX_IPV4)) {
+			PMD_LOG_ERR(TX, "Ethernet type is ipv4, tx offload "
+				"missing `RTE_MBUF_F_TX_IPV4` flag.");
+			ret = -1;
+			goto end;
+		}
+		if (m->ol_flags & RTE_MBUF_F_TX_IPV6) {
+			PMD_LOG_ERR(TX, "Ethernet type is ipv4, tx "
+				"offload contains wrong `RTE_MBUF_F_TX_IPV6` flag");
+			ret = -1;
+			goto end;
+		}
+	} else if (info->ethertype == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6)) {
+		if (!(m->ol_flags & RTE_MBUF_F_TX_IPV6)) {
+			PMD_LOG_ERR(TX, "Ethernet type is ipv6, tx offload "
+				"missing `RTE_MBUF_F_TX_IPV6` flag.");
+			ret = -1;
+			goto end;
+		}
+		if (m->ol_flags & RTE_MBUF_F_TX_IPV4) {
+			PMD_LOG_ERR(TX, "Ethernet type is ipv6, tx offload "
+				"contains wrong `RTE_MBUF_F_TX_IPV4` flag");
+			ret = -1;
+			goto end;
+		}
+	}
+	ret = 0;
+
+end:
+	return ret;
+}
+
+static inline void parse_ipv4(struct rte_ipv4_hdr *ipv4_hdr, struct offload_info *info)
+{
+	struct rte_tcp_hdr *tcp_hdr;
+
+	info->l3_len   = rte_ipv4_hdr_len(ipv4_hdr);
+	info->l4_proto = ipv4_hdr->next_proto_id;
+
+	if (info->l4_proto == IPPROTO_TCP) {
+		tcp_hdr = (struct rte_tcp_hdr *)((char *)ipv4_hdr + info->l3_len);
+		info->l4_len = (tcp_hdr->data_off & 0xf0) >> 2;
+	} else if (info->l4_proto == IPPROTO_UDP) {
+		info->l4_len = sizeof(struct rte_udp_hdr);
+	} else {
+		info->l4_len = 0;
+	}
+}
+
+static inline void parse_ipv6(struct rte_ipv6_hdr *ipv6_hdr, struct offload_info *info)
+{
+	struct rte_tcp_hdr *tcp_hdr;
+
+	info->l3_len   = sizeof(struct rte_ipv6_hdr);
+	info->l4_proto = ipv6_hdr->proto;
+
+	if (info->l4_proto == IPPROTO_TCP) {
+		tcp_hdr = (struct rte_tcp_hdr *)((char *)ipv6_hdr + info->l3_len);
+		info->l4_len = (tcp_hdr->data_off & 0xf0) >> 2;
+	} else if (info->l4_proto == IPPROTO_UDP) {
+		info->l4_len = sizeof(struct rte_udp_hdr);
+	} else {
+		info->l4_len = 0;
+	}
+}
+
+static inline void parse_ethernet(struct rte_ether_hdr *eth_hdr, struct offload_info *info)
+{
+	struct rte_ipv4_hdr *ipv4_hdr;
+	struct rte_ipv6_hdr *ipv6_hdr;
+	struct rte_vlan_hdr *vlan_hdr;
+
+	info->l2_len = sizeof(struct rte_ether_hdr);
+	info->ethertype = eth_hdr->ether_type;
+
+	while (info->ethertype == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ||
+		   info->ethertype == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ)) {
+		vlan_hdr = (struct rte_vlan_hdr *)
+			((char *)eth_hdr + info->l2_len);
+		info->l2_len   += sizeof(struct rte_vlan_hdr);
+		info->ethertype = vlan_hdr->eth_proto;
+	}
+
+	switch (info->ethertype) {
+	case RTE_STATIC_BSWAP16(RTE_ETHER_TYPE_IPV4):
+		ipv4_hdr = (struct rte_ipv4_hdr *)((char *)eth_hdr + info->l2_len);
+		parse_ipv4(ipv4_hdr, info);
+		break;
+	case RTE_STATIC_BSWAP16(RTE_ETHER_TYPE_IPV6):
+		ipv6_hdr = (struct rte_ipv6_hdr *)((char *)eth_hdr + info->l2_len);
+		parse_ipv6(ipv6_hdr, info);
+		break;
+	default:
+		info->l4_len = 0;
+		info->l3_len = 0;
+		info->l4_proto = 0;
+		break;
+	}
+}
+
+static inline void update_tunnel_outer(struct offload_info *info)
+{
+	info->is_tunnel       = 1;
+	info->outer_ethertype = info->ethertype;
+	info->outer_l2_len    = info->l2_len;
+	info->outer_l3_len    = info->l3_len;
+	info->outer_l4_proto  = info->l4_proto;
+}
+
+static inline void parse_gtp(struct rte_udp_hdr *udp_hdr, struct offload_info *info)
+{
+	struct rte_ipv4_hdr *ipv4_hdr;
+	struct rte_ipv6_hdr *ipv6_hdr;
+	struct rte_gtp_hdr *gtp_hdr;
+	uint8_t gtp_len = sizeof(*gtp_hdr);
+	uint8_t ip_ver;
+
+	if (udp_hdr->dst_port != rte_cpu_to_be_16(RTE_GTPC_UDP_PORT) &&
+		udp_hdr->src_port != rte_cpu_to_be_16(RTE_GTPC_UDP_PORT) &&
+		udp_hdr->dst_port != rte_cpu_to_be_16(RTE_GTPU_UDP_PORT))
+		goto end;
+
+	update_tunnel_outer(info);
+	info->l2_len = 0;
+
+	gtp_hdr = (struct rte_gtp_hdr *)((char *)udp_hdr + sizeof(*udp_hdr));
+
+	if (gtp_hdr->msg_type == 0xff) {
+		ip_ver = *(uint8_t *)((char *)udp_hdr + sizeof(*udp_hdr) + sizeof(*gtp_hdr));
+		ip_ver = (ip_ver) & 0xf0;
+
+		if (ip_ver == RTE_GTP_TYPE_IPV4) {
+			ipv4_hdr = (struct rte_ipv4_hdr *)((char *)gtp_hdr + gtp_len);
+			info->ethertype = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
+			parse_ipv4(ipv4_hdr, info);
+		} else if (ip_ver == RTE_GTP_TYPE_IPV6) {
+			ipv6_hdr = (struct rte_ipv6_hdr *)((char *)gtp_hdr + gtp_len);
+			info->ethertype = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6);
+			parse_ipv6(ipv6_hdr, info);
+		}
+	} else {
+		info->ethertype = 0;
+		info->l4_len    = 0;
+		info->l3_len    = 0;
+		info->l4_proto  = 0;
+	}
+
+	info->l2_len += RTE_ETHER_GTP_HLEN;
+
+end:
+	return;
+}
+
+static inline void parse_vxlan(struct rte_udp_hdr *udp_hdr, struct offload_info *info)
+{
+	struct rte_ether_hdr *eth_hdr;
+
+	if (udp_hdr->dst_port != rte_cpu_to_be_16(RTE_VXLAN_DEFAULT_PORT))
+		goto end;
+
+	update_tunnel_outer(info);
+
+	eth_hdr = (struct rte_ether_hdr *)((char *)udp_hdr +
+		sizeof(struct rte_udp_hdr) + sizeof(struct rte_vxlan_hdr));
+
+	parse_ethernet(eth_hdr, info);
+	info->l2_len += RTE_ETHER_VXLAN_HLEN;
+
+end:
+	return;
+}
+
+static inline void parse_vxlan_gpe(struct rte_udp_hdr *udp_hdr, struct offload_info *info)
+{
+	struct rte_ether_hdr *eth_hdr;
+	struct rte_ipv4_hdr *ipv4_hdr;
+	struct rte_ipv6_hdr *ipv6_hdr;
+	struct rte_vxlan_gpe_hdr *vxlan_gpe_hdr;
+	uint8_t vxlan_gpe_len = sizeof(*vxlan_gpe_hdr);
+
+	if (udp_hdr->dst_port != rte_cpu_to_be_16(vxlan_gpe_udp_port))
+		goto end;
+
+	vxlan_gpe_hdr = (struct rte_vxlan_gpe_hdr *)((char *)udp_hdr + sizeof(struct rte_udp_hdr));
+
+	if (!vxlan_gpe_hdr->proto || vxlan_gpe_hdr->proto == RTE_VXLAN_GPE_TYPE_IPV4) {
+		update_tunnel_outer(info);
+
+		ipv4_hdr = (struct rte_ipv4_hdr *)((char *)vxlan_gpe_hdr + vxlan_gpe_len);
+
+		parse_ipv4(ipv4_hdr, info);
+		info->ethertype = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
+		info->l2_len = 0;
+
+	} else if (vxlan_gpe_hdr->proto == RTE_VXLAN_GPE_TYPE_IPV6) {
+		update_tunnel_outer(info);
+
+		ipv6_hdr = (struct rte_ipv6_hdr *)((char *)vxlan_gpe_hdr + vxlan_gpe_len);
+
+		info->ethertype = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6);
+		parse_ipv6(ipv6_hdr, info);
+		info->l2_len = 0;
+
+	} else if (vxlan_gpe_hdr->proto == RTE_VXLAN_GPE_TYPE_ETH) {
+		update_tunnel_outer(info);
+
+		eth_hdr = (struct rte_ether_hdr *)((char *)vxlan_gpe_hdr + vxlan_gpe_len);
+
+		parse_ethernet(eth_hdr, info);
+	} else {
+		goto end;
+	}
+
+	info->l2_len += RTE_ETHER_VXLAN_GPE_HLEN;
+
+end:
+	return;
+}
+
+static inline void parse_geneve(struct rte_udp_hdr *udp_hdr, struct offload_info *info)
+{
+	struct rte_ether_hdr *eth_hdr;
+	struct rte_ipv4_hdr *ipv4_hdr;
+	struct rte_ipv6_hdr *ipv6_hdr;
+	struct rte_geneve_hdr *geneve_hdr;
+	uint16_t geneve_len;
+
+	if (udp_hdr->dst_port != rte_cpu_to_be_16(geneve_udp_port))
+		goto end;
+
+	geneve_hdr = (struct rte_geneve_hdr *)((char *)udp_hdr + sizeof(struct rte_udp_hdr));
+	geneve_len = sizeof(struct rte_geneve_hdr) + geneve_hdr->opt_len * 4;
+	if (!geneve_hdr->proto || geneve_hdr->proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)) {
+		update_tunnel_outer(info);
+		ipv4_hdr = (struct rte_ipv4_hdr *)((char *)geneve_hdr + geneve_len);
+		parse_ipv4(ipv4_hdr, info);
+		info->ethertype = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
+		info->l2_len = 0;
+	} else if (geneve_hdr->proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6)) {
+		update_tunnel_outer(info);
+		ipv6_hdr = (struct rte_ipv6_hdr *)((char *)geneve_hdr + geneve_len);
+		info->ethertype = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6);
+		parse_ipv6(ipv6_hdr, info);
+		info->l2_len = 0;
+
+	} else if (geneve_hdr->proto == rte_cpu_to_be_16(RTE_GENEVE_TYPE_ETH)) {
+		update_tunnel_outer(info);
+		eth_hdr = (struct rte_ether_hdr *)((char *)geneve_hdr + geneve_len);
+		parse_ethernet(eth_hdr, info);
+	} else {
+		goto end;
+	}
+
+	info->l2_len += (sizeof(struct rte_udp_hdr) + sizeof(struct rte_geneve_hdr) +
+		((struct rte_geneve_hdr *)geneve_hdr)->opt_len * 4);
+
+end:
+	return;
+}
+
+static inline void parse_gre(struct simple_gre_hdr *gre_hdr, struct offload_info *info)
+{
+	struct rte_ether_hdr *eth_hdr;
+	struct rte_ipv4_hdr *ipv4_hdr;
+	struct rte_ipv6_hdr *ipv6_hdr;
+	uint8_t gre_len = 0;
+
+	gre_len += sizeof(struct simple_gre_hdr);
+
+	if (gre_hdr->flags & rte_cpu_to_be_16(GRE_KEY_PRESENT))
+		gre_len += GRE_EXT_LEN;
+	if (gre_hdr->flags & rte_cpu_to_be_16(GRE_SEQUENCE_PRESENT))
+		gre_len += GRE_EXT_LEN;
+	if (gre_hdr->flags & rte_cpu_to_be_16(GRE_CHECKSUM_PRESENT))
+		gre_len += GRE_EXT_LEN;
+
+	if (gre_hdr->proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)) {
+		update_tunnel_outer(info);
+
+		ipv4_hdr = (struct rte_ipv4_hdr *)((char *)gre_hdr + gre_len);
+
+		parse_ipv4(ipv4_hdr, info);
+		info->ethertype = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
+		info->l2_len = 0;
+
+	} else if (gre_hdr->proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6)) {
+		update_tunnel_outer(info);
+
+		ipv6_hdr = (struct rte_ipv6_hdr *)((char *)gre_hdr + gre_len);
+
+		info->ethertype = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6);
+		parse_ipv6(ipv6_hdr, info);
+		info->l2_len = 0;
+
+	} else if (gre_hdr->proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_TEB)) {
+		update_tunnel_outer(info);
+
+		eth_hdr = (struct rte_ether_hdr *)((char *)gre_hdr + gre_len);
+
+		parse_ethernet(eth_hdr, info);
+	} else {
+		goto end;
+	}
+
+	info->l2_len += gre_len;
+
+end:
+	return;
+}
+
+static inline void parse_encap_ip(void *encap_ip, struct offload_info *info)
+{
+	struct rte_ipv4_hdr *ipv4_hdr = encap_ip;
+	struct rte_ipv6_hdr *ipv6_hdr = encap_ip;
+	uint8_t ip_version;
+
+	ip_version = ((ipv4_hdr->version_ihl & 0xf0) >> 4);
+
+	if (ip_version != 4 && ip_version != 6)
+		goto end;
+
+	info->is_tunnel = 1;
+	info->outer_ethertype = info->ethertype;
+	info->outer_l2_len = info->l2_len;
+	info->outer_l3_len = info->l3_len;
+
+	if (ip_version == 4) {
+		parse_ipv4(ipv4_hdr, info);
+		info->ethertype = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
+	} else {
+		parse_ipv6(ipv6_hdr, info);
+		info->ethertype = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6);
+	}
+	info->l2_len = 0;
+
+end:
+	return;
+}
+
+__rte_unused int32_t sxe2_txrx_check_mbuf(struct rte_mbuf *m)
+{
+	int32_t ret = 0;
+	struct rte_ether_hdr *eth_hdr;
+	void *l3_hdr = NULL;
+	struct offload_info info = {0};
+	uint64_t ol_flags = m->ol_flags;
+	uint64_t tunnel_type = ol_flags & RTE_MBUF_F_TX_TUNNEL_MASK;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
+	parse_ethernet(eth_hdr, &info);
+	l3_hdr = (char *)eth_hdr + info.l2_len;
+	if (info.l4_proto == IPPROTO_UDP) {
+		struct rte_udp_hdr *udp_hdr;
+
+		udp_hdr = (struct rte_udp_hdr *)((char *)l3_hdr + info.l3_len);
+		if ((info.l2_len + info.l3_len + sizeof(struct rte_udp_hdr)) > m->data_len) {
+			PMD_LOG_ERR(TX, "UDP header exceeds mbuf data length");
+			ret = -1;
+			goto end;
+		}
+		parse_gtp(udp_hdr, &info);
+		if (info.is_tunnel) {
+			if (!tunnel_type) {
+				PMD_LOG_ERR(TX, "gtp tunnel packet missing tx "
+					"offload missing `RTE_MBUF_F_TX_TUNNEL_GTP` flag");
+				ret = -1;
+				goto end;
+			}
+			if (tunnel_type != RTE_MBUF_F_TX_TUNNEL_GTP) {
+				PMD_LOG_ERR(TX, "gtp tunnel packet, tx offload has wrong "
+					"`%s` flag correct is `RTE_MBUF_F_TX_TUNNEL_GTP` flag",
+				rte_get_tx_ol_flag_name(tunnel_type));
+				ret = -1;
+				goto end;
+			}
+			goto check_len;
+		}
+		parse_vxlan_gpe(udp_hdr, &info);
+		if (info.is_tunnel) {
+			if (!tunnel_type) {
+				PMD_LOG_ERR(TX, "vxlan gpe tunnel packet missing tx "
+					"offload missing `RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE` flag");
+				ret = -1;
+				goto end;
+			}
+			if (tunnel_type != RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE) {
+				PMD_LOG_ERR(TX, "vxlan gpe tunnel packet, tx offload has "
+					"wrong `%s` flag correct is `RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE` flag",
+				rte_get_tx_ol_flag_name(tunnel_type));
+				ret = -1;
+				goto end;
+			}
+			goto check_len;
+		}
+		parse_vxlan(udp_hdr, &info);
+		if (info.is_tunnel) {
+			if (!tunnel_type) {
+				PMD_LOG_ERR(TX, "vxlan tunnel packet missing tx "
+					"offload missing `RTE_MBUF_F_TX_TUNNEL_VXLAN` flag");
+				ret = -1;
+				goto end;
+			}
+			if (tunnel_type != RTE_MBUF_F_TX_TUNNEL_VXLAN) {
+				PMD_LOG_ERR(TX, "vxlan tunnel packet, tx offload has "
+					"wrong `%s` flag correct is `RTE_MBUF_F_TX_TUNNEL_VXLAN` flag",
+				rte_get_tx_ol_flag_name(tunnel_type));
+				ret = -1;
+				goto end;
+			}
+			goto check_len;
+		}
+		parse_geneve(udp_hdr, &info);
+		if (info.is_tunnel) {
+			if (!tunnel_type) {
+				PMD_LOG_ERR(TX, "geneve tunnel packet missing tx "
+					"offload missing `RTE_MBUF_F_TX_TUNNEL_GENEVE` flag");
+				ret = -1;
+				goto end;
+			}
+			if (tunnel_type != RTE_MBUF_F_TX_TUNNEL_GENEVE) {
+				PMD_LOG_ERR(TX, "geneve tunnel packet, tx offload has "
+					"wrong `%s` flag correct is `RTE_MBUF_F_TX_TUNNEL_GENEVE` flag",
+				rte_get_tx_ol_flag_name(tunnel_type));
+				ret = -1;
+				goto end;
+			}
+			goto check_len;
+		}
+
+		if (unlikely(RTE_ETH_IS_TUNNEL_PKT(m->packet_type) != 0)) {
+			PMD_LOG_ERR(TX, "Unknown tunnel packet UDP dst port:%u",
+				    udp_hdr->dst_port);
+			ret = -1;
+			goto end;
+		}
+	} else if (info.l4_proto == TX_IPPROTO_GRE) {
+		struct simple_gre_hdr *gre_hdr;
+
+		gre_hdr = (struct simple_gre_hdr *)((char *)l3_hdr + info.l3_len);
+		parse_gre(gre_hdr, &info);
+		if (info.is_tunnel) {
+			if (!tunnel_type) {
+				PMD_LOG_ERR(TX, "gre tunnel packet missing tx "
+					"offload missing `RTE_MBUF_F_TX_TUNNEL_GRE` flag.");
+				ret = -1;
+				goto end;
+			}
+			if (tunnel_type != RTE_MBUF_F_TX_TUNNEL_GRE) {
+				PMD_LOG_ERR(TX, "gre tunnel packet, tx offload has "
+					"wrong `%s` flag, correct is `RTE_MBUF_F_TX_TUNNEL_GRE` flag",
+				rte_get_tx_ol_flag_name(tunnel_type));
+				ret = -1;
+				goto end;
+			}
+			goto check_len;
+		}
+	} else if (info.l4_proto == TX_IPPROTO_IPIP) {
+		void *encap_ip_hdr;
+
+		encap_ip_hdr = (char *)l3_hdr + info.l3_len;
+		parse_encap_ip(encap_ip_hdr, &info);
+		if (info.is_tunnel) {
+			if (!tunnel_type) {
+				PMD_LOG_ERR(TX, "Ipip tunnel packet missing tx "
+					"offload missing `RTE_MBUF_F_TX_TUNNEL_IPIP` flag");
+				ret = -1;
+				goto end;
+			}
+			if (tunnel_type != RTE_MBUF_F_TX_TUNNEL_IPIP) {
+				PMD_LOG_ERR(TX, "Ipip tunnel packet, tx offload has "
+					"wrong `%s` flag, correct is `RTE_MBUF_F_TX_TUNNEL_IPIP` flag",
+				rte_get_tx_ol_flag_name(tunnel_type));
+				ret = -1;
+				goto end;
+			}
+			goto check_len;
+		}
+	}
+
+check_len:
+	if (check_mbuf_len(&info, m) != 0) {
+		ret = -1;
+		goto end;
+	}
+	ret = check_ether_type(&info, m);
+
+end:
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_txrx_check_mbuf.h b/drivers/net/sxe2/sxe2_txrx_check_mbuf.h
new file mode 100644
index 0000000000..98197f85d9
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_txrx_check_mbuf.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_TXRX_CHECK_MBUF_H__
+#define __SXE2_TXRX_CHECK_MBUF_H__
+
+#include <rte_common.h>
+#include <rte_net.h>
+#include <rte_vect.h>
+#include <rte_malloc.h>
+#include <rte_memzone.h>
+#include <ethdev_driver.h>
+
+struct offload_info {
+	uint16_t ethertype;
+	uint8_t  gso_enable;
+	uint16_t l2_len;
+	uint16_t l3_len;
+	uint16_t l4_len;
+	uint8_t  l4_proto;
+	uint8_t  is_tunnel;
+	uint16_t outer_ethertype;
+	uint16_t outer_l2_len;
+	uint16_t outer_l3_len;
+	uint8_t  outer_l4_proto;
+	uint16_t tso_segsz;
+	uint16_t tunnel_tso_segsz;
+	uint32_t pkt_len;
+};
+
+struct simple_gre_hdr {
+	uint16_t flags;
+	uint16_t proto;
+};
+
+__rte_unused int32_t sxe2_txrx_check_mbuf(struct rte_mbuf *m);
+#endif /* __SXE2_TXRX_CHECK_MBUF_H__ */
-- 
2.52.0


^ permalink raw reply related

* [PATCH v5 19/23] net/sxe2: implement private dump info
From: liujie5 @ 2026-06-22  9:27 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260622092324.3091185-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

This patch implements the 'eth_dev_priv_dump' ops for the sxe2 PMD.
This interface allows applications to dump driver-specific internal
state and configuration information to a file stream.

The output includes:
- capabilities.
- device base info.
- device args info.
- device filter info.
- reprenstor info.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/net/sxe2/meson.build        |   1 +
 drivers/net/sxe2/sxe2_dump.c        | 287 ++++++++++++++++++++++++++++
 drivers/net/sxe2/sxe2_dump.h        |  12 ++
 drivers/net/sxe2/sxe2_ethdev.c      |   3 +
 drivers/net/sxe2/sxe2_ethdev_repr.c |   3 +
 5 files changed, 306 insertions(+)
 create mode 100644 drivers/net/sxe2/sxe2_dump.c
 create mode 100644 drivers/net/sxe2/sxe2_dump.h

diff --git a/drivers/net/sxe2/meson.build b/drivers/net/sxe2/meson.build
index 65286299aa..d653d071a9 100644
--- a/drivers/net/sxe2/meson.build
+++ b/drivers/net/sxe2/meson.build
@@ -77,4 +77,5 @@ sources += files(
         'sxe2_flow_parse_action.c',
         'sxe2_flow_parse_pattern.c',
         'sxe2_flow_parse_engine.c',
+        'sxe2_dump.c',
 )
diff --git a/drivers/net/sxe2/sxe2_dump.c b/drivers/net/sxe2/sxe2_dump.c
new file mode 100644
index 0000000000..d43473e083
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_dump.c
@@ -0,0 +1,287 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#include <rte_malloc.h>
+#include <arpa/inet.h>
+
+#include "sxe2_common_log.h"
+#include "sxe2_ethdev.h"
+#include "sxe2_dump.h"
+#include "sxe2_stats.h"
+
+static void
+sxe2_dump_dev_feature_capability(FILE *file, struct rte_eth_dev *dev)
+{
+	uint32_t i;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	const struct {
+		uint32_t cap_bit;
+		const char *name;
+	} caps_name[] = {
+		{SXE2_DEV_CAPS_OFFLOAD_L2, "L2"},
+		{SXE2_DEV_CAPS_OFFLOAD_VLAN, "VLAN"},
+		{SXE2_DEV_CAPS_OFFLOAD_IPSEC, "IPSEC"},
+		{SXE2_DEV_CAPS_OFFLOAD_RSS, "RSS"},
+		{SXE2_DEV_CAPS_OFFLOAD_FNAV, "FNAV"},
+		{SXE2_DEV_CAPS_OFFLOAD_TM, "TM"},
+		{SXE2_DEV_CAPS_OFFLOAD_PTP, "PTP"},
+	};
+	if (adapter->is_dev_repr)
+		goto l_end;
+
+	fprintf(file, "  - Dev Capability:\n");
+	for (i = 0; i < RTE_DIM(caps_name); i++) {
+		fprintf(file, "\t  -- support %s: %s\n", caps_name[i].name,
+			(adapter->cap_flags & caps_name[i].cap_bit) ? "Yes" :
+									 "No");
+	}
+l_end:
+	return;
+}
+
+static void
+sxe2_dump_device_basic_info(FILE *file, struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	fprintf(file,
+		"  - Device Base Info:\n"
+		"\t  -- name: %s\n"
+		"\t  -- pf_idx: %u port_idx: %u\n"
+		"\t  -- tx_mode_flags: 0x%x rx_mode_flags: 0x%x\n"
+		"\t  -- flow_isolate_cfg: 0x%x flow_isolated: 0x%x\n"
+		"\t  -- dev_type: 0x%x is_switchdev: 0x%x\n"
+		"\t  -- is_dev_repr: 0x%x dev_port_id: 0x%x\n"
+		"\t  -- dev_flags: 0x%x\n"
+		"\t  -- intr_conf lsc: %u rxq: %u rmv: %u\n",
+		dev->data->name,
+		adapter->pf_idx, adapter->port_idx,
+		adapter->tx_mode_flags, adapter->rx_mode_flags,
+		adapter->flow_isolate_cfg, adapter->flow_isolated,
+		adapter->dev_type, adapter->switchdev_info.is_switchdev,
+		adapter->is_dev_repr, adapter->dev_port_id,
+		dev->data->dev_flags,
+		dev->data->dev_conf.intr_conf.lsc,
+		dev->data->dev_conf.intr_conf.rxq,
+		dev->data->dev_conf.intr_conf.rmv);
+}
+
+static void
+sxe2_dump_dev_args_info(FILE *file, struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	if (adapter->is_dev_repr)
+		goto l_end;
+
+	fprintf(file,
+		"  - Device Args Info:\n"
+		"\t  -- no_sched_mode: %s\n"
+		"\t  -- flow-duplicate-pattern: %u\n"
+		"\t  -- fnav-stat-type: %u\n"
+		"\t  -- sched_layer_mode: %u\n"
+		"\t  -- rx_low_latency: %s\n"
+		"\t  -- function-flow-direct: %s\n",
+		adapter->devargs.no_sched_mode ? "On" : "Off",
+		adapter->devargs.flow_dup_pattern_mode,
+		adapter->devargs.fnav_stat_type,
+		adapter->devargs.sched_layer_mode,
+		adapter->devargs.rx_low_latency ? "On" : "Off",
+		adapter->devargs.func_flow_direct_en ? "On" : "Off");
+l_end:
+	return;
+}
+
+static void sxe2_dump_filter_info(FILE *file, struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	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->is_dev_repr)
+		goto l_end;
+
+	fprintf(file,
+		"  - Device Filter Info:\n"
+		"\t  -- cur_promisc:0x%x hw_promisc:0x%x\n"
+		"\t  -- unicast_num: %u multicast_num: %u\n"
+		"\t  -- vlan_num: %u filter_on: %u hw_filter_on: %u\n"
+		"\t  -- vlan max_cnt: %u cnt: %u\n"
+		"\t  -- tpid: 0x%x vid: 0x%x\n"
+		"\t  -- vlan_outer_insert: 0x%x vlan_outer_strip: 0x%x\n"
+		"\t  -- vlan_inner_insert: 0x%x vlan_inner_strip: 0x%x\n",
+		adapter->filter_ctxt.cur_promisc_flags,
+		adapter->filter_ctxt.hw_promisc_flags,
+		adapter->filter_ctxt.uc_num,
+		adapter->filter_ctxt.mc_num,
+		adapter->filter_ctxt.vlan_num,
+		adapter->filter_ctxt.vlan_info.filter_on,
+		adapter->filter_ctxt.vlan_info.hw_filter_on,
+		adapter->filter_ctxt.vlan_info.max_cnt,
+		adapter->filter_ctxt.vlan_info.cnt,
+		adapter->filter_ctxt.vlan_info.tpid,
+		adapter->filter_ctxt.vlan_info.vid,
+		adapter->filter_ctxt.vlan_info.outer_insert,
+		adapter->filter_ctxt.vlan_info.outer_strip,
+		adapter->filter_ctxt.vlan_info.inner_insert,
+		adapter->filter_ctxt.vlan_info.inner_strip);
+
+	if (adapter->filter_ctxt.uc_num > 0) {
+		fprintf(file,
+			"\t  -- Unicast entry:\n");
+		RTE_TAILQ_FOREACH_SAFE(mac_entry, &adapter->filter_ctxt.uc_list, next,
+				       next_mac_entry) {
+			fprintf(file,
+				"\t  -- addr: %02x:%02x:%02x:%02x:%02x:%02x hw status:%u "
+				"default:%u\n",
+				mac_entry->mac_addr.addr_bytes[0],
+				mac_entry->mac_addr.addr_bytes[1],
+				mac_entry->mac_addr.addr_bytes[2],
+				mac_entry->mac_addr.addr_bytes[3],
+				mac_entry->mac_addr.addr_bytes[4],
+				mac_entry->mac_addr.addr_bytes[5],
+				mac_entry->hw_config,
+				mac_entry->default_config);
+		}
+	}
+
+	if (adapter->filter_ctxt.mc_num > 0) {
+		fprintf(file,
+			"\t  -- Multicast entry:\n");
+		RTE_TAILQ_FOREACH_SAFE(mac_entry, &adapter->filter_ctxt.mc_list,
+				       next, next_mac_entry) {
+			fprintf(file,
+				"\t  -- addr: %02x:%02x:%02x:%02x:%02x:%02x "
+				"hw status:%u default:%u\n",
+				mac_entry->mac_addr.addr_bytes[0],
+				mac_entry->mac_addr.addr_bytes[1],
+				mac_entry->mac_addr.addr_bytes[2],
+				mac_entry->mac_addr.addr_bytes[3],
+				mac_entry->mac_addr.addr_bytes[4],
+				mac_entry->mac_addr.addr_bytes[5],
+				mac_entry->hw_config,
+				mac_entry->default_config);
+		}
+	}
+
+	if (adapter->filter_ctxt.vlan_num > 0) {
+		fprintf(file,
+			"\t  -- Vlan entry:\n");
+		RTE_TAILQ_FOREACH_SAFE(vlan_entry, &adapter->filter_ctxt.vlan_list,
+			next, next_vlan_entry) {
+			fprintf(file,
+				"\t  -- vlan tpid:0x%04x vid:0x%04x prio:%d "
+				"hw status:%u default:%u\n",
+				vlan_entry->vlan_info.tpid,
+				vlan_entry->vlan_info.vid,
+				vlan_entry->vlan_info.prio,
+				vlan_entry->hw_config,
+				vlan_entry->default_config);
+		}
+	}
+l_end:
+	return;
+}
+
+static const char *sxe2_vsi_id_str(uint16_t vsi_id, char *buf, size_t len)
+{
+	if (vsi_id == SXE2_INVALID_VSI_ID)
+		return "NA";
+
+	snprintf(buf, len, "%u", vsi_id);
+	return buf;
+}
+
+static void
+sxe2_dump_switchdev_info(FILE *file, struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	uint32_t idx;
+	char k_vsi_buf[16];
+	char u_vsi_buf[16];
+
+	if (adapter->is_dev_repr && adapter->repr_priv_data) {
+		fprintf(file,
+			"  - Reprenstor Info:\n"
+			"\t  -- repr_id: %u\n"
+			"\t  -- repr_q_id: %u\n"
+			"\t  -- repr_pf_id: %u\n"
+			"\t  -- repr_vf_id: %u\n"
+			"\t  -- repr_vf_vsi_id: %u\n"
+			"\t  -- repr_vf_k_vsi_id: %s\n"
+			"\t  -- repr_vf_u_vsi_id: %s\n",
+			adapter->repr_priv_data->repr_id,
+			adapter->repr_priv_data->repr_q_id,
+			adapter->repr_priv_data->repr_pf_id,
+			adapter->repr_priv_data->repr_vf_id,
+			adapter->repr_priv_data->repr_vf_vsi_id,
+			sxe2_vsi_id_str(adapter->repr_priv_data->repr_vf_k_vsi_id,
+					k_vsi_buf, sizeof(k_vsi_buf)),
+			sxe2_vsi_id_str(adapter->repr_priv_data->repr_vf_u_vsi_id,
+					u_vsi_buf, sizeof(u_vsi_buf)));
+		goto l_end;
+	}
+	if (adapter->switchdev_info.is_switchdev) {
+		fprintf(file,
+			"  - Switchdev Info:\n"
+			"\t  -- primary:0x%x\n"
+			"\t  -- representor: 0x%x\n"
+			"\t  -- port_name_type: 0x%x\n"
+			"\t  -- nb_vf: %u nb_repr_vf: %u\n",
+			adapter->switchdev_info.primary,
+			adapter->switchdev_info.representor,
+			adapter->switchdev_info.port_name_type,
+			adapter->repr_ctxt.nb_vf,
+			adapter->repr_ctxt.nb_repr_vf);
+		if (adapter->repr_ctxt.nb_vf > 0) {
+			fprintf(file,
+				"\t  -- vf entry:\n");
+			for (idx = 0; idx < adapter->repr_ctxt.nb_vf; idx++) {
+				fprintf(file,
+					"\t  -- func_id:%u vsi_type:%u kernel_vsi_id:%u dpdk_vsi_id:%u\n",
+					adapter->repr_ctxt.repr_vf_id[idx].func_id,
+					adapter->repr_ctxt.repr_vf_id[idx].vsi_type,
+					adapter->repr_ctxt.repr_vf_id[idx].kernel_vsi_id,
+					adapter->repr_ctxt.repr_vf_id[idx].dpdk_vsi_id);
+			}
+		}
+	}
+
+l_end:
+	return;
+}
+
+int32_t sxe2_eth_dev_priv_dump(struct rte_eth_dev *dev, FILE *file)
+{
+	char *buf = NULL;
+	size_t size = 0;
+	FILE *str;
+	int32_t ret = -1;
+
+	str = open_memstream(&buf, &size);
+	if (!str) {
+		PMD_LOG_ERR(DRV, "fopen fail.");
+		goto l_end;
+	}
+
+	sxe2_dump_dev_feature_capability(str, dev);
+	sxe2_dump_device_basic_info(str, dev);
+	sxe2_dump_dev_args_info(str, dev);
+	sxe2_dump_filter_info(str, dev);
+	sxe2_dump_switchdev_info(str, dev);
+
+	(void)fflush(str);
+
+	(void)fwrite(buf, 1, size, file);
+	(void)fflush(file);
+
+	ret = 0;
+
+	(void)fclose(str);
+	free(buf);
+l_end:
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_dump.h b/drivers/net/sxe2/sxe2_dump.h
new file mode 100644
index 0000000000..05d6db9b3d
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_dump.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_DUMP_H__
+#define __SXE2_DUMP_H__
+
+#include <ethdev_driver.h>
+
+int32_t sxe2_eth_dev_priv_dump(struct rte_eth_dev *dev, FILE *file);
+
+#endif /* __SXE2_DUMP_H__ */
diff --git a/drivers/net/sxe2/sxe2_ethdev.c b/drivers/net/sxe2/sxe2_ethdev.c
index 5bb57331ed..c140c26be1 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -37,6 +37,7 @@
 #include "sxe2_host_regs.h"
 #include "sxe2_switchdev.h"
 #include "sxe2_ioctl_chnl_func.h"
+#include "sxe2_dump.h"
 #include "sxe2_ethdev_repr.h"
 #include "sxe2vf_regs.h"
 #include "sxe2_switchdev.h"
@@ -195,6 +196,8 @@ static const struct eth_dev_ops sxe2_eth_dev_ops = {
 
 	.get_module_info            = sxe2_get_module_info,
 	.get_module_eeprom          = sxe2_get_module_eeprom,
+
+	.eth_dev_priv_dump          = sxe2_eth_dev_priv_dump,
 };
 
 static int32_t sxe2_dev_configure(struct rte_eth_dev *dev)
diff --git a/drivers/net/sxe2/sxe2_ethdev_repr.c b/drivers/net/sxe2/sxe2_ethdev_repr.c
index 15b839bb74..f32318b731 100644
--- a/drivers/net/sxe2/sxe2_ethdev_repr.c
+++ b/drivers/net/sxe2/sxe2_ethdev_repr.c
@@ -11,6 +11,7 @@
 #include "sxe2_txrx.h"
 #include "sxe2_switchdev.h"
 #include "sxe2_mp.h"
+#include "sxe2_dump.h"
 #include "sxe2_stats.h"
 #include "sxe2_flow.h"
 
@@ -236,6 +237,8 @@ static const struct eth_dev_ops sxe2_switchdev_repr_dev_ops = {
 	.allmulticast_enable        = sxe2_repr_allmulti_enable,
 	.allmulticast_disable       = sxe2_repr_allmulti_disable,
 
+	.eth_dev_priv_dump          = sxe2_eth_dev_priv_dump,
+
 	.stats_get                  = sxe2_stats_info_get,
 	.stats_reset                = sxe2_stats_info_reset,
 	.xstats_get                 = sxe2_xstats_info_get,
-- 
2.52.0


^ permalink raw reply related

* [PATCH v5 18/23] net/sxe2: support SFP module info and EEPROM access
From: liujie5 @ 2026-06-22  9:27 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260622092324.3091185-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

This patch implements 'get_module_info' and 'get_module_eeprom'
ops for the sxe2 PMD. These interfaces allow applications to retrieve
the type of the plugged-in optical module and read its internal
EEPROM data.

The implementation utilizes the shared SFP header definitions to
parse the module ID, connector type, and encoding. It supports
reading the standard 256-byte EEPROM maps (SFF-8472 for SFP and
SFF-8636 for QSFP) via hardware-specific access commands.

Key features:
- Identify module types (SFP/SFP+/QSFP/QSFP28).
- Support standard EEPROM data retrieval for diagnostic tools.
- Add boundary checks to ensure safe I2C memory access.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/net/sxe2/sxe2_cmd_chnl.c |  46 +++++
 drivers/net/sxe2/sxe2_cmd_chnl.h |   3 +
 drivers/net/sxe2/sxe2_drv_cmd.h  |  18 ++
 drivers/net/sxe2/sxe2_ethdev.c   | 298 +++++++++++++++++++++++++++++++
 drivers/net/sxe2/sxe2_ethdev.h   |   9 +
 5 files changed, 374 insertions(+)

diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.c b/drivers/net/sxe2/sxe2_cmd_chnl.c
index 926eaee062..43e8c59487 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.c
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.c
@@ -1833,3 +1833,49 @@ int32_t sxe2_drv_srcvsi_prune_config(struct sxe2_adapter *adapter,
 
 	return ret;
 }
+
+int32_t sxe2_drv_sfp_eeprom_read(struct sxe2_adapter *adapter, struct sxe2_sfp_read_info *sfp_info)
+{
+	int32_t ret = -1;
+	struct sxe2_drv_sfp_req req = {0};
+	struct sxe2_drv_sfp_resp *resp = NULL;
+	struct sxe2_drv_cmd_params cmd = {0};
+
+	resp = rte_zmalloc("read sfp data", sizeof(*resp) + sfp_info->len, 0);
+	if (!resp) {
+		PMD_LOG_ERR(DRV, "Alloc memory failed");
+		ret = -ENOMEM;
+		goto l_end;
+	}
+
+	req.is_wr = false;
+	req.is_qsfp = sfp_info->is_qsfp;
+	req.page_cnt = rte_cpu_to_le_16(sfp_info->page_cnt);
+	req.offset = rte_cpu_to_le_16(sfp_info->offset);
+	req.data_len = rte_cpu_to_le_16(sfp_info->len);
+	req.bus_addr = rte_cpu_to_le_16(sfp_info->bus_addr);
+
+	PMD_DEV_LOG_INFO(adapter, DRV, "is_qsfp=%u, page_cnt=%u, offset=%u, datalen=%u, "
+			 "bus_addr=%u", sfp_info->is_qsfp, sfp_info->page_cnt, sfp_info->offset,
+			 sfp_info->len, sfp_info->bus_addr);
+
+	sxe2_drv_cmd_params_fill(adapter, &cmd, SXE2_DRV_CMD_OPT_EEP_GET,
+				 &req, sizeof(req),
+				 resp, sizeof(*resp) + sfp_info->len);
+	ret = sxe2_drv_cmd_exec(adapter->cdev, &cmd);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to read sfp, ret=%d", ret);
+		goto l_end;
+	}
+
+	ret = 0;
+	rte_memcpy(sfp_info->data, resp->data, sfp_info->len);
+
+l_end:
+	if (resp) {
+		rte_free(resp);
+		resp = NULL;
+	}
+
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.h b/drivers/net/sxe2/sxe2_cmd_chnl.h
index 97007c7cfa..988d4b458b 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.h
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.h
@@ -167,4 +167,7 @@ int32_t sxe2_drv_flow_fnav_query_stat(struct sxe2_adapter *adapter,
 int32_t sxe2_drv_srcvsi_prune_config(struct sxe2_adapter *adapter,
 		uint16_t *vsi_list, uint16_t vsi_cnt, bool set);
 
+int32_t sxe2_drv_sfp_eeprom_read(struct sxe2_adapter *adapter,
+		struct sxe2_sfp_read_info *sfp_info);
+
 #endif /* SXE2_CMD_CHNL_H */
diff --git a/drivers/net/sxe2/sxe2_drv_cmd.h b/drivers/net/sxe2/sxe2_drv_cmd.h
index 3e0b70ab02..3fabf351af 100644
--- a/drivers/net/sxe2/sxe2_drv_cmd.h
+++ b/drivers/net/sxe2/sxe2_drv_cmd.h
@@ -633,6 +633,24 @@ struct __rte_aligned(4) __rte_packed_begin sxe2_drv_udp_tunnel_resp {
 	uint8_t rsv;
 } __rte_packed_end;
 
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_sfp_req {
+	uint8_t is_wr;
+	uint8_t is_qsfp;
+	uint16_t bus_addr;
+	uint16_t page_cnt;
+	uint16_t offset;
+	uint16_t data_len;
+	uint16_t rvd;
+	uint8_t data[];
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_sfp_resp {
+	uint8_t is_wr;
+	uint8_t is_qsfp;
+	uint16_t data_len;
+	uint8_t data[];
+} __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 eb0d09eb39..5bb57331ed 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -40,6 +40,7 @@
 #include "sxe2_ethdev_repr.h"
 #include "sxe2vf_regs.h"
 #include "sxe2_switchdev.h"
+#include "sxe2_msg.h"
 
 #define SXE2_PCI_VENDOR_ID_1    0x1ff2
 #define SXE2_PCI_DEVICE_ID_PF_1 0x10b1
@@ -123,6 +124,10 @@ static int32_t sxe2_udp_tunnel_port_del(struct rte_eth_dev *dev,
 					struct rte_eth_udp_tunnel *tunnel_udp);
 static int32_t sxe2_fw_version_string_get(struct rte_eth_dev *dev,
 				      char *fw_version, size_t fw_size);
+static int32_t sxe2_get_module_info(struct rte_eth_dev *dev,
+				struct rte_eth_dev_module_info *info);
+static int32_t sxe2_get_module_eeprom(struct rte_eth_dev *dev,
+				  struct rte_dev_eeprom_info *info);
 
 static const struct eth_dev_ops sxe2_eth_dev_ops = {
 	.dev_configure              = sxe2_dev_configure,
@@ -187,6 +192,9 @@ static const struct eth_dev_ops sxe2_eth_dev_ops = {
 	.fw_version_get             = sxe2_fw_version_string_get,
 
 	.get_monitor_addr           = sxe2_get_monitor_addr,
+
+	.get_module_info            = sxe2_get_module_info,
+	.get_module_eeprom          = sxe2_get_module_eeprom,
 };
 
 static int32_t sxe2_dev_configure(struct rte_eth_dev *dev)
@@ -292,6 +300,296 @@ static int32_t sxe2_dev_start(struct rte_eth_dev *dev)
 	return ret;
 }
 
+static int32_t sxe2_sfp_type_get(struct sxe2_adapter *adapter, uint8_t *type)
+{
+	int32_t ret = -1;
+	struct sxe2_sfp_read_info sfp_info;
+
+	memset(&sfp_info, 0, sizeof(sfp_info));
+	sfp_info.bus_addr = SXE2_SFP_E2P_I2C_7BIT_ADDR0;
+	sfp_info.len = 1;
+	sfp_info.data = type;
+	sfp_info.offset = 0;
+	sfp_info.page_cnt = 0;
+	sfp_info.is_qsfp = false;
+
+	ret = sxe2_drv_sfp_eeprom_read(adapter, &sfp_info);
+	if (ret)
+		goto l_end;
+
+	ret = 0;
+	PMD_LOG_INFO(DRV, "Get sfp type success, type=%u", *type);
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_sfp_module_info_get(struct sxe2_adapter *adapter,
+						  struct rte_eth_dev_module_info *info)
+{
+	int32_t ret = -1;
+	bool page_swap = false;
+	uint8_t sff8472_rev = 0;
+	uint8_t addr_mode = 0;
+	struct sxe2_sfp_read_info sfp_info;
+
+	memset(&sfp_info, 0, sizeof(sfp_info));
+	sfp_info.bus_addr = SXE2_SFP_E2P_I2C_7BIT_ADDR0;
+	sfp_info.is_qsfp = false;
+	sfp_info.len = 1;
+	sfp_info.data = &sff8472_rev;
+	sfp_info.offset = SXE2_MODULE_SFF_8472_COMP;
+	sfp_info.page_cnt = 0;
+
+	ret = sxe2_drv_sfp_eeprom_read(adapter, &sfp_info);
+	if (ret) {
+		ret = -EIO;
+		PMD_LOG_ERR(DRV, "Failed to read 8472 protocol, ret=%d", ret);
+		goto l_end;
+	}
+
+	sfp_info.data = &addr_mode;
+	sfp_info.offset = SXE2_MODULE_SFF_8472_SWAP;
+
+	ret = sxe2_drv_sfp_eeprom_read(adapter, &sfp_info);
+	if (ret) {
+		ret = -EIO;
+		PMD_LOG_ERR(DRV, "Failed to read A2 page, ret=%d", ret);
+		goto l_end;
+	}
+
+	if (addr_mode & SXE2_MODULE_SFF_ADDR_MODE) {
+		PMD_LOG_ERR(DRV, "address change required to access page 0xA2, "
+			    "but not supported. please report the module "
+			    "type to the driver maintainers.");
+		page_swap = true;
+	}
+
+	PMD_LOG_INFO(DRV, "Read sfp module info, sff_8472=%u, a2_page=%u, swap_page=%d",
+		     sff8472_rev, addr_mode, page_swap);
+
+	if (sff8472_rev == SXE2_MODULE_SFF_8472_UNSUP ||
+	    page_swap ||
+	    !(addr_mode & SXE2_MODULE_SFF_DDM_IMPLEMENTED)) {
+		info->type = SXE2_MODULE_SFF_8079;
+		info->eeprom_len = SXE2_MODULE_SFF_8079_LEN;
+	} else {
+		info->type = SXE2_MODULE_SFF_8472;
+		info->eeprom_len = SXE2_MODULE_SFF_8472_LEN;
+	}
+
+	ret = 0;
+
+l_end:
+	return ret;
+}
+
+static int32_t
+sxe2_qsfp_module_info_get(struct sxe2_adapter *adapter, struct rte_eth_dev_module_info *info)
+{
+	int32_t ret = -1;
+	uint8_t sff8636_rev = 0;
+	struct sxe2_sfp_read_info sfp_info;
+
+	memset(&sfp_info, 0, sizeof(sfp_info));
+	sfp_info.bus_addr = SXE2_SFP_E2P_I2C_7BIT_ADDR0;
+	sfp_info.is_qsfp = true;
+	sfp_info.len = 1;
+	sfp_info.data = &sff8636_rev;
+	sfp_info.offset = SXE2_MODULE_REVISION_ADDR;
+	sfp_info.page_cnt = 0;
+
+	ret = sxe2_drv_sfp_eeprom_read(adapter, &sfp_info);
+	if (ret) {
+		ret = -EIO;
+		PMD_LOG_ERR(DRV, "Failed to read 8636 protocol, ret=%d", ret);
+		goto l_end;
+	}
+
+	if (sff8636_rev > 0x02) {
+		info->type = SXE2_MODULE_SFF_8636;
+		info->eeprom_len = SXE2_MODULE_SFF_8636_MAX_LEN;
+	} else {
+		info->type = SXE2_MODULE_SFF_8436;
+		info->eeprom_len = SXE2_MODULE_SFF_8436_MAX_LEN;
+	}
+
+l_end:
+	return ret;
+}
+
+static int32_t
+sxe2_get_module_info(struct rte_eth_dev *dev, struct rte_eth_dev_module_info *info)
+{
+	int32_t ret = -1;
+	uint8_t type = 0;
+	struct sxe2_adapter *adapter = dev->data->dev_private;
+
+	ret = sxe2_sfp_type_get(adapter, &type);
+	if (ret) {
+		ret = -EIO;
+		PMD_LOG_ERR(DRV, "Failed to read sfp type, ret=%d", ret);
+		goto l_end;
+	}
+
+	switch (type) {
+	case SXE2_MODULE_SFF_SFP_TYPE:
+		ret = sxe2_sfp_module_info_get(adapter, info);
+		if (ret)
+			goto l_end;
+		break;
+	case SXE2_MODULE_TYPE_QSFP_PLUS:
+	case SXE2_MODULE_TYPE_QSFP28:
+		ret = sxe2_qsfp_module_info_get(adapter, info);
+		if (ret)
+			goto l_end;
+		break;
+	default:
+		ret = -ENXIO;
+		PMD_LOG_ERR(DRV, "Invalid sfp type, type=%d.", type);
+		goto l_end;
+	}
+
+	PMD_LOG_INFO(DRV, "sfp eeprom type=%x, eeprom len=%d.", info->type, info->eeprom_len);
+
+l_end:
+	return ret;
+}
+
+static int32_t
+sxe2_get_sfp_eeprom(struct sxe2_adapter *adapter, struct sxe2_sfp_read_info *sfp_info)
+{
+	int32_t ret = -1;
+	uint16_t ori_len = sfp_info->len;
+	uint16_t ori_offset = sfp_info->offset;
+
+	if ((ori_len + ori_offset) > SXE2_SFP_EEP_LEN_MAX) {
+		sfp_info->len = (uint16_t)(SXE2_SFP_EEP_LEN_MAX - ori_offset);
+		ret = sxe2_drv_sfp_eeprom_read(adapter, sfp_info);
+		if (ret)
+			goto l_end;
+		sfp_info->bus_addr = SXE2_SFP_E2P_I2C_7BIT_ADDR1;
+		sfp_info->len = (uint16_t)(ori_len - (SXE2_SFP_EEP_LEN_MAX - ori_offset));
+		sfp_info->data = (uint8_t *)(sfp_info->data) + (SXE2_SFP_EEP_LEN_MAX - ori_offset);
+		sfp_info->offset = 0;
+		sfp_info->page_cnt = 0;
+		ret = sxe2_drv_sfp_eeprom_read(adapter, sfp_info);
+	} else {
+		ret = sxe2_drv_sfp_eeprom_read(adapter, sfp_info);
+	}
+
+l_end:
+	if (ret)
+		PMD_LOG_ERR(DRV, "Failed to read sfp.");
+	return ret;
+}
+
+static int32_t
+sxe2_get_qsfp_eeprom(struct sxe2_adapter *adapter,
+					  struct sxe2_sfp_read_info *sfp_info)
+{
+	int32_t ret = -1;
+	uint16_t ori_len = sfp_info->len;
+	uint16_t ori_offset = sfp_info->offset;
+	uint16_t read_len = 0;
+	uint16_t remain_len = 0;
+
+	if ((ori_len + ori_offset) > SXE2_SFP_EEP_LEN_MAX) {
+		sfp_info->len = (uint16_t)(SXE2_SFP_EEP_LEN_MAX - ori_offset);
+		ret = sxe2_drv_sfp_eeprom_read(adapter, sfp_info);
+		if (ret)
+			goto l_end;
+
+		do {
+			read_len = read_len + sfp_info->len;
+			sfp_info->data = (uint8_t *)(sfp_info->data) + sfp_info->len;
+			sfp_info->offset = SXE2_QSFP_PAGE_OFST_START;
+			sfp_info->page_cnt++;
+			remain_len = (uint16_t)(ori_len - read_len);
+			sfp_info->len = (remain_len > SXE2_QSFP_PAGE_OFST_START) ?
+					SXE2_QSFP_PAGE_OFST_START : remain_len;
+			ret = sxe2_drv_sfp_eeprom_read(adapter, sfp_info);
+			if (ret)
+				goto l_end;
+		} while (remain_len > SXE2_QSFP_PAGE_OFST_START);
+	} else {
+		ret = sxe2_drv_sfp_eeprom_read(adapter, sfp_info);
+	}
+
+l_end:
+	if (ret)
+		PMD_LOG_ERR(DRV, "Failed to read sfp.");
+	return ret;
+}
+
+static int32_t
+sxe2_get_module_eeprom(struct rte_eth_dev *dev, struct rte_dev_eeprom_info *info)
+{
+	int32_t ret = -1;
+	uint8_t type = 0;
+	struct sxe2_adapter *adapter = dev->data->dev_private;
+	struct sxe2_sfp_read_info sfp_info;
+
+	memset(&sfp_info, 0, sizeof(sfp_info));
+
+	if (!info || !info->length || !info->data ||
+			info->offset >= SXE2_SFP_EEP_LEN_MAX) {
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	PMD_LOG_INFO(DRV, "Dump sfp eeprom info offset=0x%x, len=0x%x.",
+		     info->offset, info->length);
+
+	ret = sxe2_sfp_type_get(adapter, &type);
+	if (ret) {
+		ret = -EIO;
+		PMD_LOG_ERR(DRV, "Failed to read sfp type, ret=%d", ret);
+		goto l_end;
+	}
+
+	sfp_info.bus_addr = SXE2_SFP_E2P_I2C_7BIT_ADDR0;
+	sfp_info.len = info->length;
+	sfp_info.data = info->data;
+	sfp_info.offset = info->offset;
+	sfp_info.page_cnt = 0;
+
+	switch (type) {
+	case SXE2_MODULE_SFF_SFP_TYPE:
+		if (info->length > SXE2_SFP_EEP_LEN_MAX * 2) {
+			ret = -EINVAL;
+			PMD_LOG_ERR(DRV, "sfp read size[%u] > eeprom max size[%d], ret=%d",
+				    info->length, SXE2_SFP_EEP_LEN_MAX * 2, ret);
+			goto l_end;
+		}
+		sfp_info.is_qsfp = false;
+		ret = sxe2_get_sfp_eeprom(adapter, &sfp_info);
+		if (ret)
+			goto l_end;
+		break;
+	case SXE2_MODULE_TYPE_QSFP_PLUS:
+	case SXE2_MODULE_TYPE_QSFP28:
+		if (info->length > SXE2_MODULE_SFF_8636_MAX_LEN) {
+			ret = -EINVAL;
+			PMD_LOG_ERR(DRV, "sfp read size[%u] > eeprom max size[%d], ret=%d",
+				    info->length, SXE2_SFP_EEP_LEN_MAX * 2, ret);
+			goto l_end;
+		}
+		sfp_info.is_qsfp = true;
+		ret = sxe2_get_qsfp_eeprom(adapter, &sfp_info);
+		if (ret)
+			goto l_end;
+		break;
+	default:
+		ret = -ENXIO;
+		PMD_LOG_ERR(DRV, "Invalid sfp type, type=%d.", type);
+		goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
 static enum sxe2_udp_tunnel_protocol
 sxe2_udp_tunnel_type_rte_to_sxe2(enum rte_eth_tunnel_type rte_type)
 {
diff --git a/drivers/net/sxe2/sxe2_ethdev.h b/drivers/net/sxe2/sxe2_ethdev.h
index 0c7fc56ec5..a68b95c0d0 100644
--- a/drivers/net/sxe2/sxe2_ethdev.h
+++ b/drivers/net/sxe2/sxe2_ethdev.h
@@ -274,6 +274,15 @@ struct sxe2_sched_hw_cap {
 	uint8_t adj_lvl;
 };
 
+struct sxe2_sfp_read_info {
+	uint8_t *data;
+	uint16_t offset;
+	uint16_t len;
+	uint16_t bus_addr;
+	uint16_t page_cnt;
+	bool is_qsfp;
+};
+
 struct sxe2_link_context {
 	rte_spinlock_t link_lock;
 	bool link_up;
-- 
2.52.0


^ permalink raw reply related

* [PATCH v5 17/23] common/sxe2: add shared SFP module definitions
From: liujie5 @ 2026-06-22  9:26 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260622092324.3091185-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

This patch adds a new shared header file 'sxe2_msg.h' which
contains definitions for SFP/SFP+ modules. This file is shared across
Firmware, Kernel driver, and DPDK PMD to ensure consistent protocol
handling.

The header includes:
- SFP EEPROM memory map offsets.
- Module type encoding definitions.

By using this shared header, the PMD can correctly identify module
capabilities and report diagnostic information in a way that is
consistent with the underlying firmware logic.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/common/sxe2/sxe2_msg.h | 118 +++++++++++++++++++++++++++++++++
 1 file changed, 118 insertions(+)
 create mode 100644 drivers/common/sxe2/sxe2_msg.h

diff --git a/drivers/common/sxe2/sxe2_msg.h b/drivers/common/sxe2/sxe2_msg.h
new file mode 100644
index 0000000000..f08944f7c9
--- /dev/null
+++ b/drivers/common/sxe2/sxe2_msg.h
@@ -0,0 +1,118 @@
+
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_MSG_H__
+#define __SXE2_MSG_H__
+
+enum sfp_type_identifier {
+	SXE2_SFP_TYPE_UNKNOWN       = 0x00,
+	SXE2_SFP_TYPE_SFP          = 0x03,
+
+	SXE2_SFP_TYPE_QSFP_PLUS    = 0x0D,
+	SXE2_SFP_TYPE_QSFP28       = 0x11,
+
+	SXE2_SFP_TYPE_MAX          = 0xFF,
+};
+
+#ifndef SFP_DEFINE
+#define SFP_DEFINE
+
+#define SXE2_SFP_EEP_WR            0x1
+#define SXE2_SFP_EEP_QSFP          0x1
+
+enum sfp_bus_addr {
+	SXE2_SFP_EEP_I2C_ADDR0 = 0xA0,
+	SXE2_SFP_EEP_I2C_ADDR1 = 0xA2,
+	SXE2_SFP_EEP_I2C_ADDR_NR = 0xFFFF,
+};
+
+struct sxe2_sfp_req {
+	uint8_t is_wr;
+	uint8_t is_qsfp;
+	uint16_t bus_addr;
+	uint16_t page_cnt;
+	uint16_t offset;
+	uint16_t data_len;
+	uint16_t rvd;
+	uint8_t data[];
+};
+
+struct sxe2_sfp_resp {
+	uint8_t is_wr;
+	uint8_t is_qsfp;
+	uint16_t data_len;
+	uint8_t data[];
+};
+
+enum sfp_page_cnt {
+	SXE2_SFP_EEP_PAGE_CNT0     = 0,
+	SXE2_SFP_EEP_PAGE_CNT1,
+	SXE2_SFP_EEP_PAGE_CNT2,
+	SXE2_SFP_EEP_PAGE_CNT3,
+	SXE2_SFP_EEP_PAGE_CNT20    = 20,
+	SXE2_SFP_EEP_PAGE_CNT21    = 21,
+
+	SXE2_SFP_EEP_PAGE_CNT_NR   = 0xFFFF,
+};
+
+#define SXE2_SFP_E2P_I2C_7BIT_ADDR0             (SXE2_SFP_EEP_I2C_ADDR0 >> 1)
+#define SXE2_SFP_E2P_I2C_7BIT_ADDR1             (SXE2_SFP_EEP_I2C_ADDR1 >> 1)
+
+#define SXE2_QSFP_PAGE_OFST_START  128
+#define SXE2_SFP_EEP_OFST_MAX      255
+#define SXE2_SFP_EEP_LEN_MAX       256
+#endif
+
+#ifndef FW_STATE_DEFINE
+#define FW_STATE_DEFINE
+
+#define SXE2_FW_STATUS_MAIN_SHIF       (16)
+#define SXE2_FW_STATUS_MAIN_MASK       (0xFF0000)
+#define SXE2_FW_STATUS_SUB_MASK        (0xFFFF)
+
+enum Sxe2FwStateMain {
+	SXE2_FW_STATE_MAIN_UNDEFINED       = 0x00,
+	SXE2_FW_STATE_MAIN_INIT            = 0x10000,
+	SXE2_FW_STATE_MAIN_RUN             = 0x20000,
+	SXE2_FW_STATE_MAIN_ABNOMAL         = 0x30000,
+};
+
+enum Sxe2FwState {
+	SXE2_FW_START_STATE_UNDEFINED = SXE2_FW_STATE_MAIN_UNDEFINED,
+	SXE2_FW_START_STATE_INIT_BASE = (SXE2_FW_STATE_MAIN_INIT + 0x1),
+	SXE2_FW_START_STATE_SCAN_DEVICE = (SXE2_FW_STATE_MAIN_INIT + 0x20),
+	SXE2_FW_START_STATE_FINISHED = (SXE2_FW_STATE_MAIN_RUN + 0x0),
+	SXE2_FW_START_STATE_UPGRADE = (SXE2_FW_STATE_MAIN_RUN + 0x1),
+	SXE2_FW_START_STATE_SYNC = (SXE2_FW_STATE_MAIN_RUN + 0x2),
+	SXE2_FW_RUNNING_STATE_ABNOMAL = (SXE2_FW_STATE_MAIN_ABNOMAL + 0x1),
+	SXE2_FW_RUNNING_STATE_ABNOMAL_CORE1 = (SXE2_FW_STATE_MAIN_ABNOMAL + 0x2),
+	SXE2_FW_RUNNING_STATE_ABNOMAL_HEART = (SXE2_FW_STATE_MAIN_ABNOMAL + 0x3),
+	SXE2_FW_START_STATE_MASK = (SXE2_FW_STATUS_MAIN_MASK | SXE2_FW_STATUS_SUB_MASK),
+};
+#endif
+
+#ifndef LED_DEFINE
+#define LED_DEFINE
+
+enum sxe2_led_mode {
+	SXE2_IDENTIFY_LED_BLINK_ON   = 0,
+	SXE2_IDENTIFY_LED_BLINK_OFF,
+	SXE2_IDENTIFY_LED_ON,
+	SXE2_IDENTIFY_LED_OFF,
+	SXE2_IDENTIFY_LED_RESET,
+};
+
+
+typedef struct sxe2_led_ctrl {
+	uint32_t mode;
+	uint32_t duration;
+} sxe2_led_ctrl_s;
+
+typedef struct sxe2_led_ctrl_resp {
+	uint32_t ack;
+} sxe2_led_ctrl_resp_s;
+#endif
+
+#endif /* __SXE2_MSG_H__ */
-- 
2.52.0


^ permalink raw reply related

* [PATCH v5 16/23] net/sxe2: implement get monitor address
From: liujie5 @ 2026-06-22  9:26 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260622092324.3091185-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 0fba1693f8..eb0d09eb39 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -185,6 +185,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 2495ab3ab7..820d4f0620 100644
--- a/drivers/net/sxe2/sxe2_rx.c
+++ b/drivers/net/sxe2/sxe2_rx.c
@@ -534,3 +534,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 v5 15/23] net/sxe2: support firmware version reading
From: liujie5 @ 2026-06-22  9:26 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260622092324.3091185-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

This patch implements the logic to retrieve the firmware version and
Build ID from the hardware during device initialization.

The version is exposed to applications through the dev_info_get API.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/net/sxe2/sxe2_ethdev.c | 35 +++++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/drivers/net/sxe2/sxe2_ethdev.c b/drivers/net/sxe2/sxe2_ethdev.c
index 08a24bba99..0fba1693f8 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -121,7 +121,8 @@ 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 int32_t sxe2_fw_version_string_get(struct rte_eth_dev *dev,
+				      char *fw_version, size_t fw_size);
 
 static const struct eth_dev_ops sxe2_eth_dev_ops = {
 	.dev_configure              = sxe2_dev_configure,
@@ -182,6 +183,8 @@ static const struct eth_dev_ops sxe2_eth_dev_ops = {
 	.xstats_reset               = sxe2_stats_info_reset,
 
 	.queue_stats_mapping_set    = sxe2_queue_stats_mapping_set,
+
+	.fw_version_get             = sxe2_fw_version_string_get,
 };
 
 static int32_t sxe2_dev_configure(struct rte_eth_dev *dev)
@@ -1576,6 +1579,36 @@ static int32_t sxe2_eth_pmd_remove(struct sxe2_common_device *cdev)
 	return ret;
 }
 
+static int32_t sxe2_fw_version_string_get(struct rte_eth_dev *dev, char *fw_version, size_t fw_size)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_fw_info *fw_info = &adapter->dev_info.fw;
+	int32_t ret_len;
+	int32_t ret;
+
+	ret_len = snprintf(fw_version, fw_size,
+			   "%u.%u.%u.%u",
+			   fw_info->main_version_id,
+			   fw_info->sub_version_id,
+			   fw_info->fix_version_id,
+			   fw_info->build_id);
+
+	if (ret_len < 0) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret_len += 1;
+	if (fw_size < (size_t)ret_len)
+		ret = -EINVAL;
+	else
+		ret = 0;
+
+out:
+	return ret;
+}
+
 static uint16_t sxe2_switchdev_repr_id_encode_get(struct sxe2_switchdev_info *switchdev_info)
 {
 	enum rte_eth_representor_type type;
-- 
2.52.0


^ permalink raw reply related

* [PATCH v5 14/23] net/sxe2: add support for custom UDP tunnel ports
From: liujie5 @ 2026-06-22  9:26 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260622092324.3091185-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             | 200 ++++++++++++++++++++-
 drivers/net/sxe2/sxe2_ethdev.h             |   9 +
 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, 557 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 dba4e85789..3e0b70ab02 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 e6fb9ae594..08a24bba99 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -39,10 +39,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
@@ -116,6 +117,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,
@@ -163,6 +169,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,
 
@@ -227,6 +236,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.");
@@ -272,6 +287,182 @@ 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;
+}
+
+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)
 {
@@ -1307,15 +1498,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 06adbf448f..0c7fc56ec5 100644
--- a/drivers/net/sxe2/sxe2_ethdev.h
+++ b/drivers/net/sxe2/sxe2_ethdev.h
@@ -318,6 +318,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;
@@ -369,6 +370,14 @@ 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_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 a662bb4375..21925f1cd4 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 v5 13/23] drivers: add support for VF representors
From: liujie5 @ 2026-06-22  9:26 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260622092324.3091185-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

Add support for VF representors in sxe2 PMD. This allows the host
application (e.g., OVS-DPDK) to control and monitor virtual functions
through a dedicated ethdev on the PF (Physical Function) side.

Key changes include:
- Added representor enumeration and identification logic.
- Implemented representor-specific dev_ops (link update, stats, etc.).
- Configured back-channel communication between PF and VF for control
  messages.
- Supported the "-a <DBDF>,representor=[0-N]" EAL parameter to
  instantiate representor ports.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/common/sxe2/sxe2_common.c          |   46 +
 drivers/common/sxe2/sxe2_common.h          |    2 +
 drivers/net/sxe2/meson.build               |    6 +
 drivers/net/sxe2/sxe2_cmd_chnl.c           |  311 +++-
 drivers/net/sxe2/sxe2_cmd_chnl.h           |   28 +
 drivers/net/sxe2/sxe2_drv_cmd.h            |   55 +-
 drivers/net/sxe2/sxe2_ethdev.c             |  223 ++-
 drivers/net/sxe2/sxe2_ethdev.h             |   11 +
 drivers/net/sxe2/sxe2_ethdev_repr.c        |  606 +++++++
 drivers/net/sxe2/sxe2_ethdev_repr.h        |   32 +
 drivers/net/sxe2/sxe2_filter.c             |  121 +-
 drivers/net/sxe2/sxe2_filter.h             |    2 +
 drivers/net/sxe2/sxe2_flow.c               | 1337 ++++++++++++++
 drivers/net/sxe2/sxe2_flow.h               |   29 +
 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 | 1822 ++++++++++++++++++++
 drivers/net/sxe2/sxe2_flow_parse_pattern.h |   40 +
 drivers/net/sxe2/sxe2_irq.c                |   54 +
 drivers/net/sxe2/sxe2_irq.h                |    4 +
 drivers/net/sxe2/sxe2_queue.c              |    6 +-
 drivers/net/sxe2/sxe2_stats.c              |   17 +-
 drivers/net/sxe2/sxe2_switchdev.c          |  332 ++++
 drivers/net/sxe2/sxe2_switchdev.h          |   33 +
 drivers/net/sxe2/sxe2_txrx.c               |    7 +
 drivers/net/sxe2/sxe2_txrx_poll.c          |    8 +
 drivers/net/sxe2/sxe2_vsi.c                |  146 ++
 drivers/net/sxe2/sxe2_vsi.h                |   12 +-
 30 files changed, 6591 insertions(+), 23 deletions(-)
 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_flow.c
 create mode 100644 drivers/net/sxe2/sxe2_flow.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_switchdev.c
 create mode 100644 drivers/net/sxe2/sxe2_switchdev.h

diff --git a/drivers/common/sxe2/sxe2_common.c b/drivers/common/sxe2/sxe2_common.c
index f5ab8e9fa2..c000a55cd0 100644
--- a/drivers/common/sxe2/sxe2_common.c
+++ b/drivers/common/sxe2/sxe2_common.c
@@ -169,6 +169,37 @@ static int32_t sxe2_parse_class_type(const char *key, const char *value, void *a
 	return ret;
 }
 
+static int32_t sxe2_parse_driver(const char *key, const char *value, void *args)
+{
+	int32_t ret = -EINVAL;
+
+	if (value == NULL || args == NULL) {
+		ret = 0;
+		goto l_end;
+	}
+
+	if (strcmp(value, "sxe2") != 0) {
+		PMD_LOG_ERR(COM, "%s: \"%s\" is not a valid driver.",
+			key, value);
+		goto l_end;
+	}
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_parse_representor(const char *key, const char *value, void *args)
+{
+	int32_t ret = 0;
+
+	if (value == NULL || args == NULL)
+		goto l_end;
+
+	PMD_LOG_INFO(COM, "representor arg %s: \"%s\".", key, value);
+
+l_end:
+	return ret;
+}
+
 static int32_t sxe2_common_device_setup(struct sxe2_common_device *cdev)
 {
 	struct rte_pci_device *pci_dev = RTE_BUS_DEVICE(cdev->dev, *pci_dev);
@@ -394,6 +425,21 @@ static int32_t sxe2_common_pci_probe(struct rte_pci_driver *pci_drv __rte_unused
 			goto l_free_args;
 		}
 
+		ret = sxe2_kvargs_process(kv_info_p, SXE2_DEVARGS_KEY_DRIVER,
+				sxe2_parse_driver, NULL);
+		if (ret < 0) {
+			PMD_LOG_ERR(COM, "Unsupported sxe2 driver name: %s",
+				rte_dev->devargs->args);
+			goto l_free_args;
+		}
+
+		ret = sxe2_kvargs_process(kv_info_p, SXE2_DEVARGS_KEY_REPR,
+				sxe2_parse_representor, NULL);
+		if (ret < 0) {
+			PMD_LOG_ERR(COM, "Unsupported sxe2 driver representor: %s",
+				rte_dev->devargs->args);
+			goto l_free_args;
+		}
 	}
 
 	cdev = sxe2_common_device_alloc(rte_dev, class_type);
diff --git a/drivers/common/sxe2/sxe2_common.h b/drivers/common/sxe2/sxe2_common.h
index 482d29a7bb..b02b6317da 100644
--- a/drivers/common/sxe2/sxe2_common.h
+++ b/drivers/common/sxe2/sxe2_common.h
@@ -18,6 +18,8 @@
 	((cdev)->config.cmd_fd)
 
 #define SXE2_DEVARGS_KEY_CLASS "class"
+#define SXE2_DEVARGS_KEY_DRIVER "driver"
+#define SXE2_DEVARGS_KEY_REPR "representor"
 
 struct sxe2_class_driver;
 
diff --git a/drivers/net/sxe2/meson.build b/drivers/net/sxe2/meson.build
index 4565046eae..65286299aa 100644
--- a/drivers/net/sxe2/meson.build
+++ b/drivers/net/sxe2/meson.build
@@ -71,4 +71,10 @@ sources += files(
         'sxe2_mp.c',
         'sxe2_stats.c',
         'sxe2_irq.c',
+        'sxe2_switchdev.c',
+        'sxe2_ethdev_repr.c',
+        'sxe2_flow.c',
+        'sxe2_flow_parse_action.c',
+        'sxe2_flow_parse_pattern.c',
+        'sxe2_flow_parse_engine.c',
 )
diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.c b/drivers/net/sxe2/sxe2_cmd_chnl.c
index d1f15084ed..6e2dd139a5 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.c
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.c
@@ -64,6 +64,23 @@ int32_t sxe2_drv_dev_caps_get(struct sxe2_adapter *adapter, struct sxe2_drv_dev_
 	return ret;
 }
 
+int32_t sxe2_drv_switchdev_info_get(struct sxe2_adapter *adapter,
+				    struct sxe2_switchdev_info *switchdev_info)
+{
+	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_DEV_GET_SWITCHDEV_INFO,
+				 NULL, 0, switchdev_info,
+				 sizeof(struct sxe2_switchdev_info));
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, DRV, "get switchdev info failed, ret=%d", ret);
+
+	return ret;
+}
+
 int32_t sxe2_drv_dev_info_get(struct sxe2_adapter *adapter,
 				struct sxe2_drv_dev_info_resp *dev_info_resp)
 {
@@ -167,7 +184,11 @@ static int32_t sxe2_rxq_ctxt_cfg_fill(struct sxe2_rx_queue *rxq,
 	req->q_cnt = rxq_cnt;
 	req->max_frame_size = dev_data->mtu + SXE2_ETH_OVERHEAD;
 
-	ctxt->queue_id = rxq->queue_id;
+	if (adapter->is_dev_repr)
+		ctxt->queue_id = adapter->repr_priv_data->repr_q_id;
+	else
+		ctxt->queue_id = rxq->queue_id;
+
 	ctxt->depth = rxq->ring_depth;
 	ctxt->buf_len = RTE_ALIGN(rxq->rx_buf_len, SXE2_RXQ_CTXT_CFG_BUF_LEN_ALIGN);
 	ctxt->dma_addr = rxq->base_addr;
@@ -241,7 +262,10 @@ static void sxe2_txq_ctxt_cfg_fill(struct sxe2_tx_queue *txq,
 		ctxt = &req->cfg[q_idx];
 		ctxt->depth = txq[q_idx].ring_depth;
 		ctxt->dma_addr = txq[q_idx].base_addr;
-		ctxt->queue_id = txq[q_idx].queue_id;
+		if (adapter->is_dev_repr)
+			ctxt->queue_id = adapter->repr_priv_data->repr_q_id;
+		else
+			ctxt->queue_id = txq[q_idx].queue_id;
 
 		ctxt->sched_mode = sxe2_sched_mode_get(adapter);
 	}
@@ -288,7 +312,10 @@ int32_t sxe2_drv_rxq_switch(struct sxe2_adapter *adapter, struct sxe2_rx_queue *
 	struct sxe2_drv_q_switch_req req;
 
 	req.vsi_id = rte_cpu_to_le_16(rxq->vsi->vsi_id);
-	req.q_idx = rxq->queue_id;
+	if (adapter->is_dev_repr)
+		req.q_idx = adapter->repr_priv_data->repr_q_id;
+	else
+		req.q_idx = rxq->queue_id;
 
 	req.is_enable  = (uint8_t)enable;
 	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_RXQ_DISABLE,
@@ -310,7 +337,10 @@ int32_t sxe2_drv_txq_switch(struct sxe2_adapter *adapter, struct sxe2_tx_queue *
 	struct sxe2_drv_q_switch_req req;
 
 	req.vsi_id = rte_cpu_to_le_16(txq->vsi->vsi_id);
-	req.q_idx = txq->queue_id;
+	if (adapter->is_dev_repr)
+		req.q_idx = adapter->repr_priv_data->repr_q_id;
+	else
+		req.q_idx = txq->queue_id;
 
 	req.is_enable  = (uint8_t)enable;
 	req.sched_mode = sxe2_sched_mode_get(adapter);
@@ -326,6 +356,37 @@ int32_t sxe2_drv_txq_switch(struct sxe2_adapter *adapter, struct sxe2_tx_queue *
 	return ret;
 }
 
+int32_t sxe2_drv_vsi_info_get(struct sxe2_adapter *adapter, struct sxe2_vsi *vsi)
+{
+	int32_t ret = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_drv_vsi_info_get_req vsi_info_get_req = {0};
+	struct sxe2_drv_vsi_info_get_resp vsi_info_get_resp = {0};
+
+	vsi_info_get_req.vsi_id = vsi->vsi_id;
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_VSI_INFO_GET,
+			&vsi_info_get_req, sizeof(vsi_info_get_req),
+			&vsi_info_get_resp, sizeof(vsi_info_get_resp));
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "switchdev cpvsi info get failed, ret=%d", ret);
+		goto l_end;
+	}
+
+	vsi->txqs.q_cnt = vsi_info_get_resp.used_queues.queues_cnt;
+	vsi->txqs.base_idx_in_func = vsi_info_get_resp.used_queues.base_idx_in_pf;
+
+	vsi->rxqs.q_cnt = vsi_info_get_resp.used_queues.queues_cnt;
+	vsi->rxqs.base_idx_in_func = vsi_info_get_resp.used_queues.base_idx_in_pf;
+
+	vsi->irqs.avail_cnt = vsi_info_get_resp.used_msix.msix_vectors_cnt;
+	vsi->irqs.base_idx_in_pf = vsi_info_get_resp.used_msix.base_idx_in_func;
+
+l_end:
+	return ret;
+}
+
 int32_t sxe2_drv_mac_link_status_get(struct sxe2_adapter *adapter)
 {
 	int32_t ret = 0;
@@ -614,6 +675,101 @@ int32_t sxe2_drv_allmulti_config(struct sxe2_adapter *adapter, bool set)
 	return ret;
 }
 
+int32_t sxe2_drv_switchdev_uplink_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_switchdev_uplink_info switchdev_uplink_info_req = {0};
+
+	switchdev_uplink_info_req.pf_id = adapter->pf_idx;
+	switchdev_uplink_info_req.is_set = set;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_SWITCH_UPLINK,
+				 &switchdev_uplink_info_req,
+				 sizeof(switchdev_uplink_info_req),
+				 NULL, 0);
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_WARN(adapter, DRV, "switchdev uplink info config failed, ret=%d", ret);
+
+	return ret;
+}
+
+int32_t sxe2_drv_switchdev_repr_vf_config(struct sxe2_adapter *adapter,
+				      struct sxe2_switchdev_repr_info *repr_vf,
+				      bool set)
+{
+	int32_t ret = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_switchdev_repr_info switchdev_repr_info_req = {0};
+
+	switchdev_repr_info_req.pf_id = adapter->pf_idx;
+	switchdev_repr_info_req.is_set = set;
+	switchdev_repr_info_req.cp_vsi_id = repr_vf->cp_vsi_id;
+	switchdev_repr_info_req.repr_pf_id = repr_vf->repr_pf_id;
+	switchdev_repr_info_req.repr_vf_id = repr_vf->repr_vf_id;
+	switchdev_repr_info_req.repr_q_id = repr_vf->repr_q_id;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_SWITCH_REPR,
+			&switchdev_repr_info_req,
+			sizeof(switchdev_repr_info_req),
+			NULL, 0);
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_WARN(adapter, DRV, "switchdev repr info config failed, ret=%d", ret);
+
+	return ret;
+}
+
+int32_t sxe2_drv_switchdev_mode_get(struct sxe2_adapter *adapter, bool *is_switchdev)
+{
+	int32_t ret = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_switchdev_mode_info switchdev_mode_info_req = {0};
+	struct sxe2_switchdev_mode_info switchdev_mode_info_resp = {0};
+
+	switchdev_mode_info_req.pf_id = adapter->pf_idx;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_SWITCH_MODE,
+				 &switchdev_mode_info_req,
+				 sizeof(switchdev_mode_info_req),
+				 &switchdev_mode_info_resp,
+				 sizeof(switchdev_mode_info_resp));
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_WARN(adapter, DRV, "switchdev mode info get failed, ret=%d", ret);
+	else
+		*is_switchdev = (bool)switchdev_mode_info_resp.is_switchdev;
+
+	return ret;
+}
+
+int32_t sxe2_drv_switchdev_cpvsi_get(struct sxe2_adapter *adapter, uint16_t *cp_vsi_id)
+{
+	int32_t ret = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_switchdev_cpvsi_info switchdev_cpvsi_resp = {0};
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_SWITCH_CPVSI,
+				 NULL, 0,
+				 &switchdev_cpvsi_resp,
+				 sizeof(switchdev_cpvsi_resp));
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_WARN(adapter, DRV, "switchdev cpvsi info get failed, ret=%d", ret);
+	else
+		*cp_vsi_id = switchdev_cpvsi_resp.cp_vsi_id;
+
+	return ret;
+}
+
 int32_t sxe2_drv_uc_config(struct sxe2_adapter *adapter, struct rte_ether_addr *addr, bool add)
 {
 	int32_t ret = 0;
@@ -1434,3 +1590,150 @@ int32_t sxe2_drv_mapping_stats_info_clear(struct rte_eth_dev *eth_dev)
 
 	return ret;
 }
+
+int32_t sxe2_drv_flow_filter_add(struct sxe2_adapter *adapter, struct sxe2_flow *flow)
+{
+	struct sxe2_drv_flow_filter_req req = { 0 };
+	struct sxe2_drv_flow_filter_resp resp = { 0 };
+	struct sxe2_drv_cmd_params cmd             = { 0 };
+	struct sxe2_common_device *cdev = adapter->cdev;
+	int32_t ret                                 = -1;
+
+	memcpy(&req.pattern_inner, &flow->pattern_inner, sizeof(req.pattern_inner));
+	memcpy(&req.pattern_outer, &flow->pattern_outer, sizeof(req.pattern_outer));
+	memcpy(&req.action, &flow->action, sizeof(req.action));
+	memcpy(&req.meta, &flow->meta, sizeof(req.meta));
+	req.engine_type = flow->engine_type;
+	req.flow_id = 0;
+
+	sxe2_drv_cmd_params_fill(adapter, &cmd, SXE2_DRV_CMD_FLOW_FILTER_ADD, &req,
+			   sizeof(req), &resp, sizeof(resp));
+	ret = sxe2_drv_cmd_exec(cdev, &cmd);
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to add flow filter, ret: %d.", ret);
+	flow->flow_id = resp.flow_id;
+	flow->create_err = ret;
+	return ret;
+}
+
+int32_t sxe2_drv_flow_filter_del(struct sxe2_adapter *adapter, struct sxe2_flow *flow)
+{
+	struct sxe2_drv_cmd_params cmd             = { 0 };
+	struct sxe2_drv_flow_filter_req req = { 0 };
+	struct sxe2_common_device *cdev = adapter->cdev;
+	int32_t ret                                 = -1;
+	memcpy(&req.pattern_inner, &flow->pattern_inner, sizeof(req.pattern_inner));
+	memcpy(&req.pattern_outer, &flow->pattern_outer, sizeof(req.pattern_outer));
+	memcpy(&req.action, &flow->action, sizeof(req.action));
+	memcpy(&req.meta, &flow->meta, sizeof(req.meta));
+	req.engine_type = flow->engine_type;
+	req.flow_id = flow->flow_id;
+
+	sxe2_drv_cmd_params_fill(adapter, &cmd, SXE2_DRV_CMD_FLOW_FILTER_DEL, &req,
+			   sizeof(req), NULL, 0);
+	ret = sxe2_drv_cmd_exec(cdev, &cmd);
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, DRV,
+			"Failed to delete flow filter, flow id: %u, ret: %d.",
+			flow->flow_id, ret);
+	return ret;
+}
+
+int32_t sxe2_drv_flow_fnav_get_stat_id(struct sxe2_adapter *adapter, uint32_t *stat_id)
+{
+	struct sxe2_drv_flow_fnav_get_stat_id_req req = { 0 };
+	struct sxe2_drv_flow_fnav_get_stat_id_resp resp = { 0 };
+	struct sxe2_drv_cmd_params cmd             = { 0 };
+	struct sxe2_common_device *cdev = adapter->cdev;
+	int32_t ret                                 = -1;
+
+	sxe2_drv_cmd_params_fill(adapter, &cmd, SXE2_DRV_CMD_FLOW_FNAV_STAT_ALLOC,
+				 &req, sizeof(req),
+				 &resp, sizeof(resp));
+	ret = sxe2_drv_cmd_exec(cdev, &cmd);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to get fnav stat id, ret: %d.", ret);
+		goto l_end;
+	}
+	*stat_id = resp.stat_id;
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_drv_flow_fnav_free_stat(struct sxe2_adapter *adapter, uint32_t stat_id)
+{
+	struct sxe2_drv_flow_fnav_free_stat_id_req req = { 0 };
+	struct sxe2_drv_cmd_params cmd             = { 0 };
+	struct sxe2_common_device *cdev = adapter->cdev;
+	int32_t ret                                 = -1;
+
+	req.stat_id = stat_id;
+	sxe2_drv_cmd_params_fill(adapter, &cmd, SXE2_DRV_CMD_FLOW_FNAV_STAT_FREE,
+				 &req, sizeof(req),
+				 NULL, 0);
+	ret = sxe2_drv_cmd_exec(cdev, &cmd);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to free fnav stat id, ret: %d.", ret);
+		goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_drv_flow_fnav_query_stat(struct sxe2_adapter *adapter,
+		struct sxe2_fnav_cid_mgr *mgr)
+{
+	struct sxe2_drv_flow_fnav_query_stat_req req = { 0 };
+	struct sxe2_drv_flow_fnav_query_stat_resp resp = { 0 };
+	struct sxe2_drv_cmd_params cmd             = { 0 };
+	struct sxe2_common_device *cdev = adapter->cdev;
+	int32_t ret                                 = -1;
+
+	req.stat_id = mgr->stat_index;
+	req.stat_ctrl = mgr->count_type;
+	req.is_clear = 1;
+
+	sxe2_drv_cmd_params_fill(adapter, &cmd, SXE2_DRV_CMD_FLOW_FNAV_STAT_QUERY,
+				 &req, sizeof(req),
+				 &resp, sizeof(resp));
+	ret = sxe2_drv_cmd_exec(cdev, &cmd);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV,
+			"Failed to query fnav stat, stat id: %u, ret: %d.",
+			req.stat_id, ret);
+		goto l_end;
+	}
+	mgr->hits += resp.stat_hits;
+	mgr->bytes += resp.stat_bytes;
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_drv_srcvsi_prune_config(struct sxe2_adapter *adapter,
+			uint16_t *vsi_list, uint16_t vsi_cnt, bool set)
+{
+	int32_t ret = 0;
+	uint16_t idx;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_srcvsi_ext_cfg_req srcvsi_list_prune_cfg_req = {0};
+
+	srcvsi_list_prune_cfg_req.vsi_id = adapter->vsi_ctxt.dpdk_vsi_id;
+	srcvsi_list_prune_cfg_req.is_add = set;
+	srcvsi_list_prune_cfg_req.srcvsi_cnt = vsi_cnt;
+	for (idx = 0; idx < vsi_cnt; idx++)
+		srcvsi_list_prune_cfg_req.srcvsi_list[idx] = vsi_list[idx];
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_VSI_SRCVSI_PRUNE,
+				 &srcvsi_list_prune_cfg_req,
+				 sizeof(srcvsi_list_prune_cfg_req),
+				 NULL, 0);
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_WARN(adapter, DRV, "srcvsi prune config 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 3eb30078e1..52cd9922ad 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.h
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.h
@@ -12,6 +12,9 @@
 int32_t sxe2_drv_dev_caps_get(struct sxe2_adapter *adapter,
 		struct sxe2_drv_dev_caps_resp *dev_caps);
 
+int32_t sxe2_drv_switchdev_info_get(struct sxe2_adapter *adapter,
+		struct sxe2_switchdev_info *switchdev_info);
+
 int32_t sxe2_drv_dev_info_get(struct sxe2_adapter *adapter,
 		struct sxe2_drv_dev_info_resp *dev_info_resp);
 
@@ -64,6 +67,8 @@ 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_vsi_info_get(struct sxe2_adapter *adapter, struct sxe2_vsi *vsi);
+
 int32_t sxe2_drv_mac_link_status_get(struct sxe2_adapter *adapter);
 
 int32_t sxe2_drv_get_mac_stats(struct sxe2_adapter *adapter);
@@ -91,6 +96,15 @@ 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_switchdev_uplink_config(struct sxe2_adapter *adapter,  bool set);
+
+int32_t sxe2_drv_switchdev_repr_vf_config(struct sxe2_adapter *adapter,
+				struct sxe2_switchdev_repr_info *repr_vf, bool set);
+
+int32_t sxe2_drv_switchdev_cpvsi_get(struct sxe2_adapter *adapter, uint16_t *cp_vsi_id);
+
+int32_t sxe2_drv_switchdev_mode_get(struct sxe2_adapter *adapter, bool *is_switchdev);
+
 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);
@@ -122,4 +136,18 @@ int32_t sxe2_drv_rxq_bind_irq(struct sxe2_adapter *adapter, uint16_t rxq_idx, ui
 
 int32_t sxe2_drv_rxq_unbind_irq(struct sxe2_adapter *adapter, uint16_t rxq_idx);
 
+int32_t sxe2_drv_flow_filter_add(struct sxe2_adapter *adapter, struct sxe2_flow *flow);
+
+int32_t sxe2_drv_flow_filter_del(struct sxe2_adapter *adapter, struct sxe2_flow *flow);
+
+int32_t sxe2_drv_flow_fnav_get_stat_id(struct sxe2_adapter *adapter, uint32_t *stat_id);
+
+int32_t sxe2_drv_flow_fnav_free_stat(struct sxe2_adapter *adapter, uint32_t stat_id);
+
+int32_t sxe2_drv_flow_fnav_query_stat(struct sxe2_adapter *adapter,
+		struct sxe2_fnav_cid_mgr *mgr);
+
+int32_t sxe2_drv_srcvsi_prune_config(struct sxe2_adapter *adapter,
+		uint16_t *vsi_list, uint16_t vsi_cnt, bool set);
+
 #endif /* SXE2_CMD_CHNL_H */
diff --git a/drivers/net/sxe2/sxe2_drv_cmd.h b/drivers/net/sxe2/sxe2_drv_cmd.h
index d7822c6498..dba4e85789 100644
--- a/drivers/net/sxe2/sxe2_drv_cmd.h
+++ b/drivers/net/sxe2/sxe2_drv_cmd.h
@@ -108,7 +108,6 @@ enum sxe2_phys_port_name_type {
 	SXE2_PHYS_PORT_NAME_TYPE_LEGACY,
 	SXE2_PHYS_PORT_NAME_TYPE_UPLINK,
 	SXE2_PHYS_PORT_NAME_TYPE_PFVF,
-
 	SXE2_PHYS_PORT_NAME_TYPE_UNKNOWN,
 };
 
@@ -564,6 +563,60 @@ struct __rte_aligned(4) __rte_packed_begin sxe2_drv_queue_irq_bind_req {
 	uint8_t rsv[2];
 } __rte_packed_end;
 
+struct __rte_aligned(4) __rte_packed_begin sxe2_switchdev_uplink_info {
+	uint8_t pf_id;
+	uint8_t is_set;
+	uint8_t rsv[2];
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_switchdev_repr_info {
+	uint8_t pf_id;
+	uint8_t is_set;
+	uint8_t rsv[2];
+	uint16_t cp_vsi_id;
+	uint16_t repr_pf_id;
+	uint16_t repr_vf_id;
+	uint16_t repr_q_id;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_flow_filter_req {
+	uint32_t flow_id;
+	struct sxe2_flow_meta meta;
+	enum sxe2_flow_engine_type engine_type;
+	struct sxe2_flow_pattern pattern_outer;
+	struct sxe2_flow_pattern pattern_inner;
+	struct sxe2_flow_action action;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_flow_filter_resp {
+	enum sxe2_flow_engine_type engine_type;
+	uint32_t flow_id;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_flow_fnav_get_stat_id_req {
+	uint8_t need_update;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_flow_fnav_get_stat_id_resp {
+	uint32_t stat_id;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_flow_fnav_free_stat_id_req {
+	uint32_t stat_id;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_flow_fnav_query_stat_req {
+	uint32_t stat_id;
+	uint32_t stat_ctrl;
+	uint32_t is_clear;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_flow_fnav_query_stat_resp {
+	uint32_t stat_index;
+	uint64_t stat_hits;
+	uint64_t stat_bytes;
+} __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 89c6f28adc..e6fb9ae594 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -32,9 +32,13 @@
 #include "sxe2_common.h"
 #include "sxe2_common_log.h"
 #include "sxe2_mp.h"
+#include "sxe2_flow.h"
 #include "sxe2_stats.h"
 #include "sxe2_host_regs.h"
+#include "sxe2_switchdev.h"
 #include "sxe2_ioctl_chnl_func.h"
+#include "sxe2_ethdev_repr.h"
+#include "sxe2vf_regs.h"
 
 #define SXE2_PCI_VENDOR_ID_1    0x1ff2
 #define SXE2_PCI_DEVICE_ID_PF_1 0x10b1
@@ -49,6 +53,8 @@
 
 #define SXE2_PCI_VENDOR_ID_206F 0x206f
 
+#define SXE2_FLOW_DUP_PATTERN_DEFAULT  1
+
 static const struct rte_pci_id pci_id_sxe2_tbl[] = {
 	{ RTE_PCI_DEVICE(SXE2_PCI_VENDOR_ID_1, SXE2_PCI_DEVICE_ID_PF_1)},
 	{ RTE_PCI_DEVICE(SXE2_PCI_VENDOR_ID_1, SXE2_PCI_DEVICE_ID_VF_1)},
@@ -82,6 +88,27 @@ static struct sxe2_pci_map_addr_info sxe2_net_map_addr_info_pf[SXE2_PCI_MAP_RES_
 				      .reg_width = 10},
 };
 
+static struct sxe2_pci_map_addr_info sxe2_net_map_addr_info_vf[SXE2_PCI_MAP_RES_MAX_COUNT] = {
+	[SXE2_PCI_MAP_RES_INVALID]  = {.addr_base = 0,
+				      .bar_idx = 0,
+				      .reg_width = 0},
+	[SXE2_PCI_MAP_RES_DOORBELL_TX] = {.addr_base = SXE2VF_TXQ_TAIL(0),
+				      .bar_idx = 0,
+				      .reg_width = 4},
+	[SXE2_PCI_MAP_RES_DOORBELL_RX_TAIL] = {.addr_base = SXE2VF_RXQ_TAIL(0),
+				      .bar_idx = 0,
+				      .reg_width = 4},
+	[SXE2_PCI_MAP_RES_IRQ_DYN] = {.addr_base = SXE2VF_VF_DYN_CTL(0),
+				      .bar_idx = 0,
+				      .reg_width = 4},
+	[SXE2_PCI_MAP_RES_IRQ_ITR] = {.addr_base = SXE2VF_VF_INT_ITR(0, 0),
+				      .bar_idx = 0,
+				      .reg_width = 4},
+	[SXE2_PCI_MAP_RES_IRQ_MSIX] = {.addr_base = SXE2VF_BAR4_MSIX_CTL(0),
+				      .bar_idx = 4,
+				      .reg_width = 0x10},
+};
+
 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);
@@ -136,6 +163,7 @@ 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,
 
+	.flow_ops_get               = sxe2_flow_ops_get,
 	.tm_ops_get                 = sxe2_tm_ops_get,
 
 	.stats_get                  = sxe2_stats_info_get,
@@ -555,7 +583,7 @@ static int32_t sxe2_eth_init(struct rte_eth_dev *dev)
 	return ret;
 }
 
-static void sxe2_eth_uinit(struct rte_eth_dev *dev __rte_unused)
+void sxe2_eth_uinit(struct rte_eth_dev *dev __rte_unused)
 {
 	sxe2_mac_addr_uinit(dev);
 	(void)sxe2_filter_uinit(dev);
@@ -596,6 +624,16 @@ static void sxe2_drv_dev_caps_set(struct sxe2_adapter *adapter,
 		adapter->cap_flags |= SXE2_DEV_CAPS_OFFLOAD_FC_STATE;
 }
 
+static void sxe2_sw_representor_ctx_hw_cap_set(struct sxe2_adapter *adapter,
+			struct sxe2_drv_representor_caps *repr_caps)
+{
+	adapter->repr_ctxt.nb_vf = repr_caps->cnt_repr_vf;
+	if (adapter->repr_ctxt.nb_vf > 0) {
+		memcpy(adapter->repr_ctxt.repr_vf_id, repr_caps->repr_vf_id,
+			adapter->repr_ctxt.nb_vf * sizeof(struct sxe2_drv_vsi_caps));
+	}
+}
+
 static void sxe2_sw_sched_hw_cap_set(struct sxe2_adapter *adapter,
 				     struct sxe2_txsch_caps *txsch_caps)
 {
@@ -625,20 +663,47 @@ 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_representor_ctx_hw_cap_set(adapter, &dev_caps.repr_caps);
+
 	sxe2_sw_sched_hw_cap_set(adapter, &dev_caps.txsch_caps);
 
 l_end:
 	return ret;
 }
 
+static int32_t sxe2_switchdev_info_get(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+	struct sxe2_switchdev_info switchdev_info = {0};
+
+	ret = sxe2_drv_switchdev_info_get(adapter, &switchdev_info);
+	if (ret)
+		goto l_end;
+	if (switchdev_info.primary && switchdev_info.representor) {
+		PMD_LOG_ERR(INIT, "device could not be both primary and representor");
+		ret = -ENODEV;
+		goto l_end;
+	}
+	adapter->switchdev_info = switchdev_info;
+l_end:
+	return ret;
+}
+
 static int32_t sxe2_dev_caps_get(struct sxe2_adapter *adapter)
 {
 	int32_t ret = -1;
 
 	ret = sxe2_func_caps_get(adapter);
-	if (ret)
+	if (ret) {
 		PMD_LOG_ERR(INIT, "get function caps failed, ret=%d", ret);
+		goto l_end;
+	}
 
+	ret = sxe2_switchdev_info_get(adapter);
+	if (ret)
+		PMD_LOG_ERR(INIT, "get switchdev info failed, ret=%d", ret);
+
+l_end:
 	return ret;
 }
 
@@ -924,7 +989,10 @@ int32_t sxe2_dev_pci_map_init(struct rte_eth_dev *dev)
 	bar_info[1].seg_info = seg_info;
 	map_ctxt->bar_info = bar_info;
 
-	map_ctxt->addr_info = sxe2_net_map_addr_info_pf;
+	if (adapter->dev_type == SXE2_DEV_T_VF)
+		map_ctxt->addr_info = sxe2_net_map_addr_info_vf;
+	else
+		map_ctxt->addr_info = sxe2_net_map_addr_info_pf;
 
 	ret = sxe2_dev_pci_res_seg_map(adapter, SXE2_PCI_MAP_RES_DOORBELL_TX,
 				       txq_cnt, txq_base);
@@ -1081,8 +1149,20 @@ static int32_t sxe2_sched_uinit(struct rte_eth_dev *dev)
 	return ret;
 }
 
+static int32_t sxe2_args_parse(struct rte_eth_dev *dev,
+			       __rte_unused struct sxe2_dev_kvargs_info *kvargs)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+	PMD_INIT_FUNC_TRACE();
+
+	adapter->devargs.flow_dup_pattern_mode = SXE2_FLOW_DUP_PATTERN_DEFAULT;
+
+	return ret;
+}
+
 static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
-			     struct sxe2_dev_kvargs_info *kvargs __rte_unused)
+			     struct sxe2_dev_kvargs_info *kvargs)
 {
 	int32_t ret = 0;
 
@@ -1101,6 +1181,12 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 		goto l_end;
 	}
 
+	ret = sxe2_args_parse(dev, kvargs);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to parse args, ret=[%d]", ret);
+		goto l_end;
+	}
+
 	ret = sxe2_hw_init(dev);
 	if (ret) {
 		PMD_LOG_ERR(INIT, "Failed to initialize hw, ret=[%d]", ret);
@@ -1125,6 +1211,12 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 		goto init_dev_info_err;
 	}
 
+	ret = sxe2_switchdev_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to initialize switchdev mode, ret=[%d]", ret);
+		goto init_switchdev_err;
+	}
+
 	ret = sxe2_sw_init(dev);
 	if (ret) {
 		PMD_LOG_ERR(INIT, "Failed to initialize sw parameters, ret=[%d]", ret);
@@ -1155,6 +1247,12 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 		goto init_rss_err;
 	}
 
+	ret = sxe2_flow_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to init flow, ret=%d", ret);
+		goto init_flow_err;
+	}
+
 	ret = sxe2_sched_init(dev);
 	if (ret) {
 		PMD_LOG_ERR(INIT, "Failed to init sched, ret=%d", ret);
@@ -1178,15 +1276,19 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 init_xstats_err:
 	(void)sxe2_sched_uinit(dev);
 init_sched_err:
+	(void)sxe2_flow_uninit(dev);
+init_flow_err:
 init_rss_err:
 	sxe2_security_uinit(dev);
 init_security_err:
+	sxe2_eth_uinit(dev);
+init_eth_err:
 	sxe2_intr_uninit(dev);
 init_irq_err:
 	sxe2_sw_uninit(dev);
 init_sw_err:
-	sxe2_eth_uinit(dev);
-init_eth_err:
+	(void)sxe2_switchdev_uninit(dev);
+init_switchdev_err:
 init_dev_info_err:
 	sxe2_vsi_uninit(dev);
 init_vsi_err:
@@ -1201,6 +1303,7 @@ static int32_t sxe2_dev_close(struct rte_eth_dev *dev)
 		sxe2_mp_uninit(dev);
 		goto l_end;
 	}
+	sxe2_repr_all_close(dev);
 	(void)sxe2_dev_stop(dev);
 	(void)sxe2_queues_release(dev);
 	sxe2_mp_uninit(dev);
@@ -1209,6 +1312,7 @@ static int32_t sxe2_dev_close(struct rte_eth_dev *dev)
 	sxe2_vsi_uninit(dev);
 	sxe2_security_uinit(dev);
 	sxe2_intr_uninit(dev);
+	(void)sxe2_switchdev_uninit(dev);
 	sxe2_sw_uninit(dev);
 	sxe2_eth_uinit(dev);
 	sxe2_dev_pci_map_uinit(dev);
@@ -1220,10 +1324,29 @@ static int32_t sxe2_dev_close(struct rte_eth_dev *dev)
 static int32_t sxe2_dev_uninit(struct rte_eth_dev *dev)
 {
 	int32_t ret = 0;
+	int32_t i = 0;
+	struct sxe2_adapter *adapter = NULL;
+	struct rte_eth_dev *rep_dev = NULL;
 
 	if (rte_eal_process_type() != RTE_PROC_PRIMARY)
 		goto l_end;
 
+	adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	for (i = 0; i < adapter->repr_ctxt.nb_repr_vf; i++) {
+		rep_dev = adapter->repr_ctxt.vf_rep_eth_dev[i];
+		if (rep_dev) {
+			ret = rep_dev->dev_ops->dev_close(rep_dev);
+			if (ret)
+				goto l_end;
+			if (rep_dev->intr_handle)
+				rte_intr_instance_free(rep_dev->intr_handle);
+			ret = rte_eth_dev_release_port(rep_dev);
+			if (ret)
+				goto l_end;
+			adapter->repr_ctxt.vf_rep_eth_dev[i] = NULL;
+		}
+	}
+
 	ret = sxe2_dev_close(dev);
 	if (ret) {
 		PMD_LOG_ERR(INIT, "Sxe2 dev close failed, ret=%d", ret);
@@ -1257,6 +1380,65 @@ static int32_t sxe2_eth_pmd_remove(struct sxe2_common_device *cdev)
 	return ret;
 }
 
+static uint16_t sxe2_switchdev_repr_id_encode_get(struct sxe2_switchdev_info *switchdev_info)
+{
+	enum rte_eth_representor_type type;
+	uint16_t repr = switchdev_info->vf_num;
+	uint32_t pf = switchdev_info->pf_num;
+
+	switch (switchdev_info->port_name_type) {
+	case SXE2_PHYS_PORT_NAME_TYPE_UPLINK:
+		if (!switchdev_info->representor)
+			return UINT16_MAX;
+		type = RTE_ETH_REPRESENTOR_PF;
+		pf = switchdev_info->mpesw_owner;
+		break;
+	case SXE2_PHYS_PORT_NAME_TYPE_PFVF:
+	default:
+		type = RTE_ETH_REPRESENTOR_VF;
+		break;
+	}
+
+	return SXE2_REPRESENTOR_ID(pf, type, repr);
+}
+
+static bool sxe2_switchdev_repr_match(struct sxe2_adapter *adapter,
+				   struct rte_eth_devargs *req_eth_da)
+{
+	uint32_t port_idx = 0;
+	uint32_t repr_idx;
+	uint16_t kernel_repr_id = sxe2_switchdev_repr_id_encode_get(&adapter->switchdev_info);
+	uint16_t repr_id;
+
+	switch (req_eth_da->type) {
+	case RTE_ETH_REPRESENTOR_PF:
+		break;
+	case RTE_ETH_REPRESENTOR_VF:
+		if (adapter->switchdev_info.port_name_type !=
+		SXE2_PHYS_PORT_NAME_TYPE_PFVF) {
+			rte_errno = EBUSY;
+			return false;
+		}
+		break;
+	case RTE_ETH_REPRESENTOR_NONE:
+		rte_errno = EBUSY;
+		return false;
+	default:
+		rte_errno = ENOTSUP;
+		return false;
+	}
+
+	for (repr_idx = 0; repr_idx < req_eth_da->nb_representor_ports; ++repr_idx) {
+		repr_id = SXE2_REPRESENTOR_ID(req_eth_da->ports[port_idx],
+					      req_eth_da->type,
+					      req_eth_da->representor_ports[repr_idx]);
+		if (repr_id == kernel_repr_id)
+			return true;
+	}
+	rte_errno = EBUSY;
+	return false;
+}
+
 static int32_t sxe2_eth_pmd_probe_pf(struct sxe2_common_device *cdev,
 		struct rte_eth_devargs *req_eth_da __rte_unused,
 		uint16_t owner_id __rte_unused,
@@ -1298,10 +1480,34 @@ static int32_t sxe2_eth_pmd_probe_pf(struct sxe2_common_device *cdev,
 		goto l_release_port;
 	}
 
+	if (req_eth_da->nb_representor_ports > 0) {
+		if (!adapter->switchdev_info.is_switchdev) {
+			PMD_DEV_LOG_ERR(adapter, INIT, "Representor requested but Switchdev not enabled");
+			ret = -ENOTSUP;
+			goto l_dev_uinit;
+		}
+
+		if (!sxe2_switchdev_repr_match(adapter, req_eth_da)) {
+			PMD_DEV_LOG_ERR(adapter, INIT, "Representor parameters mismatch");
+			ret = -ENOTSUP;
+			goto l_dev_uinit;
+		}
+
+		ret = sxe2_switchdev_repr_devs_init(adapter, req_eth_da);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, INIT, "Failed to init representor, ret=%d", ret);
+			goto l_dev_uinit;
+		}
+	} else {
+		PMD_DEV_LOG_DEBUG(adapter, INIT, "No representors requested, skipping.");
+	}
+
 	rte_eth_dev_probing_finish(eth_dev);
 	PMD_DEV_LOG_DEBUG(adapter, INIT, "Sxe2 eth pmd probe successful!");
 	goto l_end;
 
+l_dev_uinit:
+	(void)sxe2_dev_uninit(eth_dev);
 l_release_port:
 	(void)rte_eth_dev_release_port(eth_dev);
 l_end:
@@ -1371,6 +1577,11 @@ static struct sxe2_class_driver sxe2_eth_pmd = {
 	.intr_rmv = 1,
 };
 
+bool sxe2_ethdev_check(struct rte_eth_dev *dev)
+{
+	return !strcmp(dev->device->driver->name, "sxe2_pci");
+}
+
 RTE_INIT(rte_sxe2_pmd_init)
 {
 	sxe2_common_init();
diff --git a/drivers/net/sxe2/sxe2_ethdev.h b/drivers/net/sxe2/sxe2_ethdev.h
index 0734c10059..06adbf448f 100644
--- a/drivers/net/sxe2/sxe2_ethdev.h
+++ b/drivers/net/sxe2/sxe2_ethdev.h
@@ -65,6 +65,9 @@ enum sxe2_fnav_tunnel_flag_type {
 #define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
 #define lower_32_bits(n) ((uint32_t)((n) & 0xffffffff))
 
+#define SXE2_REPRESENTOR_ID(pf, type, repr) \
+		(((pf) << 14) + ((type) << 12) + ((repr) & 0xfff))
+
 #define SXE2_I2C_EEPROM_DEV_ADDR	0xA0
 #define SXE2_I2C_EEPROM_DEV_ADDR2	0xA2
 #define SXE2_MODULE_TYPE_SFP		0x03
@@ -309,16 +312,20 @@ struct sxe2_adapter {
 	struct sxe2_vsi_context       vsi_ctxt;
 	struct sxe2_filter_context    filter_ctxt;
 	struct sxe2_rss_context       rss_ctxt;
+	struct sxe2_flow_context      flow_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_security_ctx      security_ctx;
+	struct sxe2_repr_context      repr_ctxt;
 	struct sxe2_switchdev_info    switchdev_info;
 	bool                          rule_started;
 	bool                          flow_isolated;
+	bool                          flow_isolate_cfg;
 	uint16_t                           dev_port_id;
+	bool                          is_dev_repr;
 	uint64_t                           cap_flags;
 	enum sxe2_dev_type            dev_type;
 	struct rte_ether_addr           mac_addr;
@@ -336,6 +343,8 @@ void *sxe2_pci_map_addr_get(struct sxe2_adapter *adapter,
 			    enum sxe2_pci_map_resource res_type,
 			    uint16_t idx_in_func);
 
+bool sxe2_ethdev_check(struct rte_eth_dev *dev);
+
 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,
@@ -362,6 +371,8 @@ int32_t sxe2_dev_pci_map_init(struct rte_eth_dev *dev);
 
 void sxe2_dev_pci_map_uinit(struct rte_eth_dev *dev);
 
+void sxe2_eth_uinit(struct rte_eth_dev *dev);
+
 static inline bool
 sxe2_dev_port_vlan_check(struct rte_eth_dev *dev)
 {
diff --git a/drivers/net/sxe2/sxe2_ethdev_repr.c b/drivers/net/sxe2/sxe2_ethdev_repr.c
new file mode 100644
index 0000000000..15b839bb74
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_ethdev_repr.c
@@ -0,0 +1,606 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#include "sxe2_ethdev_repr.h"
+#include "sxe2_ethdev.h"
+#include "sxe2_common.h"
+#include "sxe2_common_log.h"
+#include "sxe2_tx.h"
+#include "sxe2_rx.h"
+#include "sxe2_txrx.h"
+#include "sxe2_switchdev.h"
+#include "sxe2_mp.h"
+#include "sxe2_stats.h"
+#include "sxe2_flow.h"
+
+static struct sxe2_pci_map_addr_info sxe2_net_map_addr_info_repr[SXE2_PCI_MAP_RES_MAX_COUNT] = {
+	{0, 0, 0},
+	{ SXE2_TXQ_LEGACY_DBLL(0), 0, 4},
+	{ SXE2_RXQ_TAIL(0), 0, 4},
+	{ SXE2_VF_DYN_CTL(0), 0, 4},
+	{ SXE2_VF_INT_ITR(0, 0), 0, 4},
+	{ SXE2_BAR4_MSIX_CTL(0), 4, 0x10},
+};
+
+static void sxe2_repr_dev_uinit(struct rte_eth_dev *dev);
+
+static int32_t sxe2_repr_promisc_enable(struct rte_eth_dev *dev __rte_unused)
+{
+	return 0;
+}
+static int32_t sxe2_repr_promisc_disable(struct rte_eth_dev *dev __rte_unused)
+{
+	return 0;
+}
+static int32_t sxe2_repr_allmulti_enable(struct rte_eth_dev *dev __rte_unused)
+{
+	return 0;
+}
+static int32_t sxe2_repr_allmulti_disable(struct rte_eth_dev *dev __rte_unused)
+{
+	return 0;
+}
+
+static int32_t sxe2_repr_dev_configure(struct rte_eth_dev *dev)
+{
+	dev->data->mtu = SXE2_FRAME_SIZE_MAX - SXE2_ETH_OVERHEAD;
+	return 0;
+}
+
+static int32_t sxe2_repr_dev_start(struct rte_eth_dev *dev)
+{
+	int32_t ret = 0;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	PMD_INIT_FUNC_TRACE();
+
+	ret = sxe2_queues_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to init queues.");
+		goto l_end;
+	}
+
+	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_repr_rxq_intr_enable(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to enable rx queue intr");
+		goto l_end;
+	}
+
+	ret = sxe2_queues_start(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "enable queues failed");
+		goto l_start_queues_err;
+	}
+
+	dev->data->dev_started = 1;
+	adapter->started = 1;
+	goto l_end;
+l_start_queues_err:
+	(void)sxe2_rxq_intr_disable(dev);
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_repr_dev_stop(struct rte_eth_dev *dev)
+{
+	int32_t ret = 0;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	PMD_INIT_FUNC_TRACE();
+
+	if (adapter->started == 0)
+		goto l_end;
+
+	sxe2_repr_rxq_intr_disable(dev);
+
+	sxe2_txqs_all_stop(dev);
+	sxe2_rxqs_all_stop(dev);
+
+	dev->data->dev_started = 0;
+	adapter->started = 0;
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_repr_dev_close(struct rte_eth_dev *dev)
+{
+	PMD_DEV_LOG_INFO(SXE2_DEV_PRIVATE_TO_ADAPTER(dev),
+			 INIT, "repr close");
+	if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
+		sxe2_mp_uninit(dev);
+		goto l_end;
+	}
+	(void)sxe2_repr_dev_stop(dev);
+	(void)sxe2_queues_release(dev);
+	sxe2_mp_uninit(dev);
+	sxe2_repr_dev_uinit(dev);
+l_end:
+	return 0;
+}
+
+static int32_t sxe2_repr_dev_infos_get(struct rte_eth_dev *dev,
+			struct rte_eth_dev_info *dev_info)
+{
+	dev_info->max_rx_queues = 1;
+	dev_info->max_tx_queues = 1;
+	dev_info->min_rx_bufsize = SXE2_MIN_BUF_SIZE;
+	dev_info->max_rx_pktlen = SXE2_FRAME_SIZE_MAX;
+	dev_info->max_lro_pkt_size = SXE2_FRAME_SIZE_MAX * SXE2_RX_LRO_DESC_MAX_NUM;
+	dev_info->max_mtu = dev_info->max_rx_pktlen - SXE2_ETH_OVERHEAD;
+	dev_info->min_mtu = RTE_ETHER_MIN_MTU;
+	dev_info->max_mac_addrs = SXE2_NUM_MACADDR_MAX;
+
+	dev_info->rx_offload_capa =
+		RTE_ETH_RX_OFFLOAD_KEEP_CRC |
+		RTE_ETH_RX_OFFLOAD_IPV4_CKSUM |
+		RTE_ETH_RX_OFFLOAD_UDP_CKSUM |
+		RTE_ETH_RX_OFFLOAD_TCP_CKSUM |
+		RTE_ETH_RX_OFFLOAD_SCTP_CKSUM |
+		RTE_ETH_RX_OFFLOAD_OUTER_IPV4_CKSUM;
+	dev_info->tx_offload_capa =
+		RTE_ETH_TX_OFFLOAD_IPV4_CKSUM |
+		RTE_ETH_TX_OFFLOAD_UDP_CKSUM |
+		RTE_ETH_TX_OFFLOAD_TCP_CKSUM |
+		RTE_ETH_TX_OFFLOAD_SCTP_CKSUM |
+		RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM |
+		RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM;
+
+	dev_info->tx_queue_offload_capa = RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE;
+
+	dev_info->default_rxconf = (struct rte_eth_rxconf) {
+		.rx_thresh = {
+			.pthresh = SXE2_DEFAULT_RX_PTHRESH,
+			.hthresh = SXE2_DEFAULT_RX_HTHRESH,
+			.wthresh = SXE2_DEFAULT_RX_WTHRESH,
+		},
+		.rx_free_thresh = SXE2_DEFAULT_RX_FREE_THRESH,
+		.rx_drop_en = 0,
+		.offloads = 0,
+	};
+
+	dev_info->default_txconf = (struct rte_eth_txconf) {
+		.tx_thresh = {
+			.pthresh = SXE2_DEFAULT_TX_PTHRESH,
+			.hthresh = SXE2_DEFAULT_TX_HTHRESH,
+			.wthresh = SXE2_DEFAULT_TX_WTHRESH,
+		},
+		.tx_free_thresh = SXE2_DEFAULT_TX_FREE_THRESH,
+		.tx_rs_thresh = SXE2_DEFAULT_TX_RSBIT_THRESH,
+		.offloads = 0,
+	};
+
+	dev_info->rx_desc_lim = (struct rte_eth_desc_lim) {
+		.nb_max = SXE2_MAX_RING_DESC,
+		.nb_min = SXE2_MIN_RING_DESC,
+		.nb_align = SXE2_ALIGN,
+	};
+
+	dev_info->tx_desc_lim = (struct rte_eth_desc_lim) {
+		.nb_max = SXE2_MAX_RING_DESC,
+		.nb_min = SXE2_MIN_RING_DESC,
+		.nb_align = SXE2_ALIGN,
+		.nb_mtu_seg_max = SXE2_TX_MTU_SEG_MAX,
+		.nb_seg_max = SXE2_MAX_RING_DESC,
+	};
+
+	dev_info->speed_capa = RTE_ETH_LINK_SPEED_10G | RTE_ETH_LINK_SPEED_25G |
+			 RTE_ETH_LINK_SPEED_50G | RTE_ETH_LINK_SPEED_100G;
+
+	dev_info->nb_rx_queues = dev->data->nb_rx_queues;
+	dev_info->nb_tx_queues = dev->data->nb_tx_queues;
+
+	dev_info->default_rxportconf.burst_size = SXE2_RX_MAX_BURST;
+	dev_info->default_txportconf.burst_size = SXE2_TX_MAX_BURST;
+	dev_info->default_rxportconf.nb_queues = 1;
+	dev_info->default_txportconf.nb_queues = 1;
+	dev_info->default_rxportconf.ring_size = SXE2_RING_SIZE_MIN;
+	dev_info->default_txportconf.ring_size = SXE2_RING_SIZE_MIN;
+
+	dev_info->rx_seg_capa.offset_allowed = false;
+
+	dev_info->rx_seg_capa.offset_align_log2 = false;
+
+	return 0;
+}
+
+static const struct eth_dev_ops sxe2_switchdev_repr_dev_ops = {
+	.dev_configure              = sxe2_repr_dev_configure,
+
+	.dev_start                  = sxe2_repr_dev_start,
+	.dev_stop                   = sxe2_repr_dev_stop,
+
+	.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,
+
+	.dev_close                  = sxe2_repr_dev_close,
+	.dev_infos_get              = sxe2_repr_dev_infos_get,
+	.dev_supported_ptypes_get   = sxe2_dev_supported_ptypes_get,
+	.link_update                = sxe2_link_update,
+
+	.promiscuous_enable         = sxe2_repr_promisc_enable,
+	.promiscuous_disable        = sxe2_repr_promisc_disable,
+	.allmulticast_enable        = sxe2_repr_allmulti_enable,
+	.allmulticast_disable       = sxe2_repr_allmulti_disable,
+
+	.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,
+};
+
+void sxe2_repr_all_close(struct rte_eth_dev *dev)
+{
+	uint16_t vf_id;
+	struct rte_eth_dev *repr_eth_dev = NULL;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	if (adapter->repr_ctxt.nb_repr_vf) {
+		for (vf_id = 0; vf_id < adapter->repr_ctxt.nb_repr_vf; vf_id++) {
+			repr_eth_dev = adapter->repr_ctxt.vf_rep_eth_dev[vf_id];
+			if (!repr_eth_dev || repr_eth_dev->data->dev_started == 0)
+				continue;
+
+			(void)rte_eth_dev_stop(repr_eth_dev->data->port_id);
+			(void)rte_eth_dev_close(repr_eth_dev->data->port_id);
+		}
+	}
+}
+
+static void sxe2_repr_adapter_init(struct rte_eth_dev *dev_repr,
+				   struct sxe2_adapter *parent_adapter,
+				   uint16_t repr_id)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev_repr);
+
+	dev_repr->data->backer_port_id = parent_adapter->dev_port_id;
+	dev_repr->data->representor_id = repr_id;
+	dev_repr->data->dev_flags |= RTE_ETH_DEV_REPRESENTOR;
+
+	adapter->is_dev_repr = true;
+	adapter->dev_port_id = dev_repr->data->port_id;
+	adapter->dev_type = parent_adapter->dev_type;
+	adapter->switchdev_info.is_switchdev = parent_adapter->switchdev_info.is_switchdev;
+	adapter->port_idx = parent_adapter->port_idx;
+	adapter->pf_idx = parent_adapter->pf_idx;
+	adapter->dev_info.pci = parent_adapter->dev_info.pci;
+	adapter->dev_info.fw = parent_adapter->dev_info.fw;
+}
+
+static int32_t sxe2_repr_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);
+		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;
+	}
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_repr_dev_pci_map_init(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *rep_adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_pci_map_context *map_ctxt = &rep_adapter->map_ctxt;
+	struct sxe2_pci_map_bar_info *bar_info = NULL;
+	struct sxe2_pci_map_segment_info *seg_info = NULL;
+	uint16_t txq_cnt = rep_adapter->q_ctxt.qp_cnt_assign;
+	uint16_t txq_base = rep_adapter->q_ctxt.base_idx_in_pf;
+	uint16_t rxq_cnt = rep_adapter->q_ctxt.qp_cnt_assign;
+	uint16_t rxq_base = rep_adapter->q_ctxt.base_idx_in_pf;
+	uint16_t irq_cnt = rep_adapter->irq_ctxt.max_cnt_hw;
+	uint16_t irq_base = rep_adapter->irq_ctxt.base_idx_in_func;
+	int32_t ret = 0;
+
+	PMD_INIT_FUNC_TRACE();
+
+	rep_adapter->dev_info.dev_data = dev->data;
+
+	map_ctxt->bar_cnt = 2;
+
+	bar_info = rte_zmalloc("repr_bar_info",
+			sizeof(struct sxe2_pci_map_bar_info) * map_ctxt->bar_cnt, 0);
+	if (bar_info == NULL) {
+		PMD_LOG_ERR(INIT, "Failed to alloc bar_info");
+		ret = -ENOMEM;
+		goto l_end;
+	}
+	bar_info[0].bar_idx = 0;
+	bar_info[0].map_cnt = SXE2_PCI_MAP_RES_MAX_COUNT;
+	seg_info = rte_zmalloc("repr_seg_info_bar0",
+			sizeof(struct sxe2_pci_map_segment_info) * bar_info[0].map_cnt, 0);
+	if (seg_info == NULL) {
+		PMD_LOG_ERR(INIT, "Failed to alloc seg_info");
+		ret = -ENOMEM;
+		goto l_free_bar;
+	}
+
+	bar_info[0].seg_info = seg_info;
+
+	bar_info[1].bar_idx = 4;
+	bar_info[1].map_cnt = SXE2_PCI_MAP_RES_MAX_COUNT;
+	seg_info = rte_zmalloc("repr_seg_info_bar4",
+			sizeof(struct sxe2_pci_map_segment_info) * bar_info[1].map_cnt,
+			0);
+	if (!seg_info) {
+		PMD_LOG_ERR(INIT, "Failed to alloc seg_info");
+		ret = -ENOMEM;
+		goto l_free_seg0;
+	}
+
+	bar_info[1].seg_info = seg_info;
+	map_ctxt->bar_info = bar_info;
+
+	map_ctxt->addr_info = sxe2_net_map_addr_info_repr;
+
+	ret = sxe2_dev_pci_res_seg_map(rep_adapter, SXE2_PCI_MAP_RES_DOORBELL_TX,
+			txq_cnt, txq_base);
+	if (ret != 0) {
+		PMD_LOG_ERR(INIT, "Failed to map txq doorbell addr, ret=%d", ret);
+		goto l_free_seg1;
+	}
+
+	ret = sxe2_dev_pci_res_seg_map(rep_adapter, SXE2_PCI_MAP_RES_DOORBELL_RX_TAIL,
+			rxq_cnt, rxq_base);
+	if (ret != 0) {
+		PMD_LOG_ERR(INIT, "Failed to map rxq tail doorbell addr, ret=%d", ret);
+		goto l_free_txq;
+	}
+
+	ret = sxe2_dev_pci_res_seg_map(rep_adapter, SXE2_PCI_MAP_RES_IRQ_DYN,
+			irq_cnt, irq_base);
+	if (ret != 0) {
+		PMD_LOG_ERR(INIT, "Failed to map irq dyn addr, ret=%d", ret);
+		goto l_free_rxq_tail;
+	}
+
+	ret = sxe2_dev_pci_res_seg_map(rep_adapter, SXE2_PCI_MAP_RES_IRQ_ITR,
+			irq_cnt, irq_base);
+	if (ret != 0) {
+		PMD_LOG_ERR(INIT, "Failed to map irq itr addr, ret=%d", ret);
+		goto l_free_irq_dyn;
+	}
+
+	ret = sxe2_dev_pci_res_seg_map(rep_adapter, SXE2_PCI_MAP_RES_IRQ_MSIX,
+			irq_cnt, irq_base);
+	if (ret != 0) {
+		PMD_LOG_ERR(INIT, "Failed to map irq msix addr, ret=%d", ret);
+		goto l_free_irq_itr;
+	}
+	goto l_end;
+
+l_free_irq_itr:
+	(void)sxe2_dev_pci_seg_unmap(rep_adapter, SXE2_PCI_MAP_RES_IRQ_ITR);
+l_free_irq_dyn:
+	(void)sxe2_dev_pci_seg_unmap(rep_adapter, SXE2_PCI_MAP_RES_IRQ_DYN);
+l_free_rxq_tail:
+	(void)sxe2_dev_pci_seg_unmap(rep_adapter, SXE2_PCI_MAP_RES_DOORBELL_RX_TAIL);
+l_free_txq:
+	(void)sxe2_dev_pci_seg_unmap(rep_adapter, SXE2_PCI_MAP_RES_DOORBELL_TX);
+l_free_seg1:
+	if (bar_info[1].seg_info) {
+		rte_free(bar_info[1].seg_info);
+		bar_info[1].seg_info = NULL;
+	}
+l_free_seg0:
+	if (bar_info[0].seg_info) {
+		rte_free(bar_info[0].seg_info);
+		bar_info[0].seg_info = NULL;
+	}
+l_free_bar:
+	if (bar_info) {
+		rte_free(bar_info);
+		bar_info = NULL;
+	}
+l_end:
+	return ret;
+}
+
+int32_t sxe2_repr_dev_init(struct rte_eth_dev *dev,
+			   struct sxe2_adapter *parent_adapter,
+			   uint16_t repr_id)
+{
+	int32_t ret = 0;
+
+	PMD_INIT_FUNC_TRACE();
+
+	sxe2_set_common_function(dev);
+
+	sxe2_repr_adapter_init(dev, parent_adapter, repr_id);
+
+	dev->dev_ops = &sxe2_switchdev_repr_dev_ops;
+
+	ret = sxe2_vsi_repr_main_vsi_create(dev, parent_adapter, repr_id);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to create representor main vsi, ret=[%d]", ret);
+		goto l_end;
+	}
+
+	ret = sxe2_switchdev_repr_private_data_init(dev, parent_adapter, repr_id);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to fill representor private data, ret=[%d]", ret);
+		goto l_init_priv_data_err;
+	}
+
+	ret = sxe2_repr_dev_pci_map_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to pci addr map, ret=[%d]", ret);
+		goto l_init_pci_error;
+	}
+
+	ret = sxe2_switchdev_dev_info_init(dev, parent_adapter);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to get device info, ret=[%d]", ret);
+		goto l_init_dev_info_err;
+	}
+
+	ret = sxe2_flow_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to init flow, ret=%d", ret);
+		goto l_init_flow_err;
+	}
+
+	ret = sxe2_repr_eth_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to init device, status = %d", ret);
+		goto l_init_eth_err;
+	}
+
+	ret = sxe2_sw_irq_ctxt_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to initialize sw parameters, ret=[%d]", ret);
+		goto l_init_sw_err;
+	}
+
+	goto l_end;
+
+l_init_sw_err:
+	sxe2_eth_uinit(dev);
+l_init_eth_err:
+	(void)sxe2_flow_uninit(dev);
+l_init_flow_err:
+l_init_dev_info_err:
+	sxe2_dev_pci_map_uinit(dev);
+l_init_pci_error:
+	(void)sxe2_switchdev_uninit(dev);
+l_init_priv_data_err:
+	sxe2_vsi_repr_main_vsi_destroy(dev);
+l_end:
+	return ret;
+}
+
+static void sxe2_repr_dev_uinit(struct rte_eth_dev *dev)
+{
+	sxe2_eth_uinit(dev);
+	(void)sxe2_flow_uninit(dev);
+	sxe2_dev_pci_map_uinit(dev);
+	(void)sxe2_switchdev_uninit(dev);
+	sxe2_vsi_repr_main_vsi_destroy(dev);
+}
+
+int32_t sxe2_switchdev_repr_devs_init(struct sxe2_adapter *adapter,
+				  struct rte_eth_devargs *req_eth_da)
+{
+	struct rte_eth_dev *eth_dev = NULL;
+	int32_t ret;
+	uint16_t repr_idx = 0, tmp_repr_idx = 0;
+	char name[RTE_ETH_NAME_MAX_LEN];
+
+	if (req_eth_da->nb_representor_ports == 0) {
+		ret = 0;
+		goto l_end;
+	}
+
+	if (req_eth_da->nb_representor_ports > adapter->repr_ctxt.nb_vf) {
+		PMD_LOG_ERR(INIT, "Failed to create repr vsi, nb_representor_ports=%d, nb_vf=%d",
+			    req_eth_da->nb_representor_ports, adapter->repr_ctxt.nb_vf);
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	ret = sxe2_other_vsi_create(adapter, req_eth_da->nb_representor_ports);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to create representor vsi, ret=%d", ret);
+		goto l_release_port;
+	}
+
+	adapter->repr_ctxt.vf_rep_eth_dev = rte_zmalloc("sxe2_repr_ethdev",
+			req_eth_da->nb_representor_ports * sizeof(struct rte_eth_dev *), 0);
+	if (adapter->repr_ctxt.vf_rep_eth_dev == NULL) {
+		PMD_LOG_ERR(INIT, "Failed to malloc representor eth dev.");
+		ret = -ENOMEM;
+		goto l_release_port;
+	}
+
+	for (repr_idx = 0; repr_idx < req_eth_da->nb_representor_ports; ++repr_idx) {
+		snprintf(name, sizeof(name), "sxe2_representor_c%dpf%d%s%u",
+			 adapter->pf_idx, adapter->pf_idx,
+			 "vf",
+			 req_eth_da->representor_ports[repr_idx]);
+
+		eth_dev = rte_eth_dev_allocate(name);
+		if (!eth_dev) {
+			ret = -ENOMEM;
+			goto l_release_port;
+		}
+		eth_dev->data->dev_private = rte_zmalloc_socket(name,
+			sizeof(struct sxe2_adapter),
+			RTE_CACHE_LINE_SIZE,
+			rte_socket_id());
+
+		if (!eth_dev->data->dev_private) {
+			rte_eth_dev_release_port(eth_dev);
+			ret = -ENOMEM;
+			goto l_release_port;
+		}
+
+		eth_dev->device = rte_eth_devices[adapter->dev_info.dev_data->port_id].device;
+
+		ret = sxe2_repr_dev_init(eth_dev, adapter,
+					 req_eth_da->representor_ports[repr_idx]);
+		if (ret) {
+			PMD_LOG_ERR(INIT, "Sxe2 dev init failed, ret=%d", ret);
+			rte_eth_dev_release_port(eth_dev);
+			goto l_release_port;
+		}
+
+		eth_dev->intr_handle = rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_SHARED);
+		if (eth_dev->intr_handle == NULL) {
+			PMD_LOG_ERR(INIT, "Sxe2 dev init representor intr_handle failed");
+			ret = -ENOMEM;
+			sxe2_repr_dev_uinit(eth_dev);
+			rte_eth_dev_release_port(eth_dev);
+			goto l_release_port;
+		}
+		adapter->repr_ctxt.vf_rep_eth_dev[repr_idx] = eth_dev;
+		rte_eth_dev_probing_finish(eth_dev);
+	}
+	adapter->repr_ctxt.nb_repr_vf = req_eth_da->nb_representor_ports;
+	goto l_end;
+
+l_release_port:
+	for (tmp_repr_idx = 0; tmp_repr_idx < repr_idx; ++tmp_repr_idx) {
+		struct rte_eth_dev *rep_dev = adapter->repr_ctxt.vf_rep_eth_dev[tmp_repr_idx];
+		if (rep_dev) {
+			sxe2_repr_dev_uinit(rep_dev);
+			if (rep_dev->intr_handle)
+				rte_intr_instance_free(rep_dev->intr_handle);
+			rte_eth_dev_release_port(rep_dev);
+			adapter->repr_ctxt.vf_rep_eth_dev[tmp_repr_idx] = NULL;
+		}
+	}
+
+	rte_free(adapter->repr_ctxt.vf_rep_eth_dev);
+	adapter->repr_ctxt.vf_rep_eth_dev = NULL;
+
+l_end:
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_ethdev_repr.h b/drivers/net/sxe2/sxe2_ethdev_repr.h
new file mode 100644
index 0000000000..71a666337f
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_ethdev_repr.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SX2_ETHDEV_REPR_H__
+#define __SX2_ETHDEV_REPR_H__
+#include <rte_compat.h>
+#include <rte_kvargs.h>
+#include <rte_time.h>
+#include <ethdev_driver.h>
+#include <ethdev_pci.h>
+#include <rte_tm_driver.h>
+#include <rte_io.h>
+#include <rte_ethdev.h>
+#include <rte_alarm.h>
+#include <rte_dev_info.h>
+
+#include "sxe2_vsi.h"
+#include "sxe2_irq.h"
+#include "sxe2_queue.h"
+struct sxe2_adapter;
+
+void sxe2_repr_all_close(struct rte_eth_dev *dev);
+
+int32_t sxe2_repr_dev_init(struct rte_eth_dev *dev,
+		       struct sxe2_adapter *parent_adapter,
+		       uint16_t repr_id);
+
+int32_t sxe2_switchdev_repr_devs_init(struct sxe2_adapter *adapter,
+				  struct rte_eth_devargs *req_eth_da);
+
+#endif /* __SX2_ETHDEV_REPR_H__ */
diff --git a/drivers/net/sxe2/sxe2_filter.c b/drivers/net/sxe2/sxe2_filter.c
index b2a726f77e..175b886aa3 100644
--- a/drivers/net/sxe2/sxe2_filter.c
+++ b/drivers/net/sxe2/sxe2_filter.c
@@ -9,6 +9,7 @@
 #include "sxe2_common_log.h"
 #include "sxe2_ethdev.h"
 #include "sxe2_cmd_chnl.h"
+#include "sxe2_switchdev.h"
 
 static struct sxe2_mac_filter *sxe2_uc_filter_find(struct sxe2_adapter *adapter,
 			struct rte_ether_addr *macaddr)
@@ -698,16 +699,96 @@ static int32_t sxe2_all_filter_hw_set(struct sxe2_adapter *adapter)
 	return ret;
 }
 
+static int32_t sxe2_uplink_hw_clear(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+
+	if (adapter->filter_ctxt.hw_uplink_config) {
+		if (adapter->dev_type == SXE2_DEV_T_PF) {
+			ret = sxe2_uplink_clear(adapter);
+			if (ret) {
+				PMD_DEV_LOG_ERR(adapter, DRV,
+						"Failed to clear uplink, ret:%d", ret);
+				goto l_end;
+			}
+			adapter->filter_ctxt.hw_uplink_config = false;
+		}
+	}
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_uplink_hw_set(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+
+	if (!adapter->filter_ctxt.hw_uplink_config) {
+		if (adapter->dev_type == SXE2_DEV_T_PF) {
+			ret = sxe2_uplink_set(adapter);
+			if (ret && ret != -EEXIST) {
+				PMD_DEV_LOG_ERR(adapter, DRV, "Failed to set uplink, ret:%d", ret);
+				goto l_end;
+			}
+			adapter->filter_ctxt.hw_uplink_config = true;
+			ret = 0;
+		}
+	}
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_repr_hw_clear(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+
+	if (adapter->filter_ctxt.hw_repr_config) {
+		if (adapter->dev_type == SXE2_DEV_T_PF ||
+			adapter->dev_type == SXE2_DEV_T_PF_BOND) {
+			ret = sxe2_repr_clear(adapter);
+			if (ret) {
+				PMD_DEV_LOG_ERR(adapter, DRV, "Failed to clear repr, ret:%d", ret);
+				goto l_end;
+			}
+			adapter->filter_ctxt.hw_repr_config = false;
+		}
+	}
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_repr_hw_set(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+
+	if (!adapter->filter_ctxt.hw_repr_config) {
+		if (adapter->dev_type == SXE2_DEV_T_PF ||
+			adapter->dev_type == SXE2_DEV_T_PF_BOND) {
+			ret = sxe2_repr_set(adapter);
+			if (ret && ret != -EEXIST) {
+				PMD_DEV_LOG_ERR(adapter, DRV, "Failed to set repr, ret:%d", ret);
+				goto l_end;
+			}
+			adapter->filter_ctxt.hw_repr_config = true;
+			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) {
+	if (!adapter->flow_isolated &&
+	    !adapter->switchdev_info.is_switchdev &&
+	    adapter->rule_started)
 		adapter->filter_ctxt.cur_l2_config = true;
-	} else {
+	else
 		adapter->filter_ctxt.cur_l2_config = false;
-	}
 
 	if (adapter->filter_ctxt.cur_l2_config !=
 	    adapter->filter_ctxt.hw_l2_config) {
@@ -724,6 +805,38 @@ int32_t sxe2_l2_rule_update(struct sxe2_adapter *adapter)
 	return ret;
 }
 
+int32_t sxe2_switchdev_rule_update(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+
+	if (!adapter->flow_isolated &&
+	    adapter->switchdev_info.is_switchdev) {
+		adapter->filter_ctxt.cur_uplink_config = true;
+		adapter->filter_ctxt.cur_repr_config = true;
+	} else {
+		adapter->filter_ctxt.cur_uplink_config = false;
+		adapter->filter_ctxt.cur_repr_config = false;
+	}
+
+	if (adapter->filter_ctxt.cur_uplink_config !=
+	    adapter->filter_ctxt.hw_uplink_config) {
+		if (adapter->filter_ctxt.cur_uplink_config)
+			ret = sxe2_uplink_hw_set(adapter);
+		else
+			ret = sxe2_uplink_hw_clear(adapter);
+	}
+
+	if (adapter->filter_ctxt.cur_repr_config !=
+	    adapter->filter_ctxt.hw_repr_config) {
+		if (adapter->filter_ctxt.cur_repr_config)
+			ret = sxe2_repr_hw_set(adapter);
+		else
+			ret = sxe2_repr_hw_clear(adapter);
+	}
+
+	return ret;
+}
+
 int32_t sxe2_filter_rule_stop(struct rte_eth_dev *dev)
 {
 	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
diff --git a/drivers/net/sxe2/sxe2_filter.h b/drivers/net/sxe2/sxe2_filter.h
index 6262e8c845..b2538ed22f 100644
--- a/drivers/net/sxe2/sxe2_filter.h
+++ b/drivers/net/sxe2/sxe2_filter.h
@@ -89,6 +89,8 @@ int32_t sxe2_l2_rule_update(struct sxe2_adapter *adapter);
 
 int32_t sxe2_filter_rule_stop(struct rte_eth_dev *dev);
 
+int32_t sxe2_switchdev_rule_update(struct sxe2_adapter *adapter);
+
 int32_t sxe2_filter_rule_start(struct rte_eth_dev *dev);
 
 int32_t sxe2_filter_init(struct rte_eth_dev *dev);
diff --git a/drivers/net/sxe2/sxe2_flow.c b/drivers/net/sxe2/sxe2_flow.c
new file mode 100644
index 0000000000..6999cb0725
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_flow.c
@@ -0,0 +1,1337 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#include <sys/queue.h>
+#include <unistd.h>
+#include "sxe2_ethdev.h"
+#include "sxe2_flow.h"
+#include "sxe2_flow_parse_pattern.h"
+#include "sxe2_flow_parse_action.h"
+#include "sxe2_flow_parse_engine.h"
+#include "sxe2_cmd_chnl.h"
+#include "sxe2_flow_public.h"
+#include "sxe2_common_log.h"
+
+static int32_t sxe2_check_para(const struct rte_flow_attr *attr,
+			   const struct rte_flow_item pattern[],
+			   const struct rte_flow_action actions[],
+			   struct rte_flow_error *error)
+{
+	int32_t ret = 0;
+	if (!pattern) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_NUM,
+				NULL, "NULL pattern.");
+		ret = -rte_errno;
+		goto l_end;
+	}
+
+	if (!actions) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION_NUM,
+				NULL, "NULL action.");
+		ret = -rte_errno;
+		goto l_end;
+	}
+
+	if (!attr) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ATTR,
+				NULL, "NULL attribute.");
+		ret = -rte_errno;
+		goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_valid_attr(const struct rte_flow_attr *attr, struct rte_flow_error *error)
+{
+	int32_t ret = 0;
+
+	if (!attr->ingress) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ATTR_INGRESS,
+				attr, "Only support ingress.");
+		ret = -rte_errno;
+		goto l_end;
+	}
+
+	if (attr->egress) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ATTR_EGRESS,
+				attr, "Not support egress.");
+		ret = -rte_errno;
+		goto l_end;
+	}
+
+	if (attr->group >= 4) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ATTR_GROUP,
+				attr, "Not support group >= 4.");
+		ret = -rte_errno;
+		goto l_end;
+	}
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_check_hdr_duplicate(struct sxe2_flow_item *item_new,
+					 struct sxe2_flow_item *item_exist)
+{
+	int32_t ret = 0;
+	uint16_t i = 0;
+	uint16_t size = sizeof(struct sxe2_flow_item);
+	union sxe2_flow_item_raw item_raw_new;
+	union sxe2_flow_item_raw item_raw_exist;
+	rte_memcpy(&item_raw_new.item, item_new, size);
+	rte_memcpy(&item_raw_exist.item, item_exist, size);
+
+	for (i = 0; i < size; i++) {
+		if (item_raw_new.raw[i] != item_raw_exist.raw[i])
+			goto l_end;
+	}
+	ret = -EEXIST;
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_check_flow_duplicate(struct sxe2_flow *flow_new,
+					      struct sxe2_flow *flow_exist)
+{
+	int32_t ret = 0;
+	int32_t ret_mask1 = 0;
+	int32_t ret_mask2 = 0;
+	int32_t ret_spec1 = 0;
+	int32_t ret_spec2 = 0;
+
+	if (flow_new->engine_type != flow_exist->engine_type)
+		goto l_end;
+	if (flow_new->meta.flow_type != flow_exist->meta.flow_type)
+		goto l_end;
+	if (!sxe2_bitmap_equal(flow_new->flow_type, flow_exist->flow_type,
+			SXE2_EXPANSION_MAX))
+		goto l_end;
+	if (flow_new->meta.flow_prio != flow_exist->meta.flow_prio)
+		goto l_end;
+
+	ret_mask1 = sxe2_flow_check_hdr_duplicate(&flow_new->pattern_outer.item_mask,
+						  &flow_exist->pattern_outer.item_mask);
+	ret_mask2 = sxe2_flow_check_hdr_duplicate(&flow_new->pattern_inner.item_mask,
+						  &flow_exist->pattern_inner.item_mask);
+
+	ret_spec1 = sxe2_flow_check_hdr_duplicate(&flow_new->pattern_outer.item_spec,
+						  &flow_exist->pattern_outer.item_spec);
+	ret_spec2 = sxe2_flow_check_hdr_duplicate(&flow_new->pattern_inner.item_spec,
+						  &flow_exist->pattern_inner.item_spec);
+
+	if (flow_new->engine_type == SXE2_FLOW_ENGINE_FNAV) {
+		if (ret_mask1 == 0 || ret_mask2 == 0) {
+			ret = -EEXIST;
+			goto l_end;
+		}
+
+		if (ret_spec1 == -EEXIST && ret_spec2 == -EEXIST) {
+			ret = -EEXIST;
+			goto l_end;
+		}
+	} else {
+		if (ret_mask1 == -EEXIST && ret_mask2 == -EEXIST &&
+			ret_spec1 == -EEXIST && ret_spec2 == -EEXIST) {
+			ret = -EEXIST;
+			goto l_end;
+		}
+	}
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_check_flow_list_duplicate(struct rte_eth_dev *dev,
+						   struct rte_flow *flow_list)
+{
+	int32_t ret = 0;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_flow *sxe2_flow_new = NULL;
+	struct rte_flow *rte_flow_exist = NULL;
+	struct sxe2_flow *sxe2_flow_exist = NULL;
+	TAILQ_FOREACH(sxe2_flow_new, &flow_list->sxe2_flow_list, next) {
+		TAILQ_FOREACH(rte_flow_exist, &adapter->flow_ctxt.rte_flow_list, next) {
+			TAILQ_FOREACH(sxe2_flow_exist, &rte_flow_exist->sxe2_flow_list, next) {
+				ret = sxe2_flow_check_flow_duplicate(sxe2_flow_new,
+								     sxe2_flow_exist);
+				if (ret != 0)
+					goto l_end;
+			}
+		}
+	}
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_check_function(struct rte_eth_dev *dev,
+				    struct rte_flow *flow_list,
+				    struct rte_flow_error *error)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_flow_list_t *sxe2_flow_list = &flow_list->sxe2_flow_list;
+	struct sxe2_flow *flow = TAILQ_FIRST(sxe2_flow_list);
+	int32_t ret = 0;
+
+	uint16_t flow_dst_vsi = UINT16_MAX;
+
+	if (adapter->dev_type == SXE2_DEV_T_VF) {
+		if (sxe2_test_bit(SXE2_FLOW_ACTION_TO_VSI, flow->action.act_types)) {
+			flow_dst_vsi = flow->action.vsi.vsi_index;
+
+			if (adapter->vsi_ctxt.dpdk_vsi_id != flow_dst_vsi &&
+				adapter->vsi_ctxt.kernel_vsi_id != flow_dst_vsi) {
+				PMD_LOG_ERR(DRV, "Failed to redirect other function");
+				rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+					"Failed to redirect other function");
+				ret = -ENOTSUP;
+				goto l_end;
+			}
+		}
+
+		if (sxe2_test_bit(SXE2_FLOW_ACTION_TO_VSI_LIST, flow->action.act_types)) {
+			PMD_LOG_ERR(DRV,
+				"Failed to redirect multiple driver or function");
+				rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+					"Failed to redirect multiple driver or function");
+				ret = -ENOTSUP;
+				goto l_end;
+		}
+
+		if (!adapter->flow_isolated &&
+			flow->engine_type == SXE2_FLOW_ENGINE_SWITCH) {
+			PMD_LOG_ERR(DRV,
+				"Failed to switch engine rules in a non-flow-isolated state");
+				rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+					"Failed to switch engine rules in a non-flow-isolated state");
+				ret = -ENOTSUP;
+				goto l_end;
+		}
+
+		if (adapter->switchdev_info.is_switchdev &&
+			flow->engine_type == SXE2_FLOW_ENGINE_SWITCH) {
+			PMD_LOG_ERR(DRV,
+				"Failed to switch engine rules in a switchdev mode state");
+				rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+					"Failed to switch engine rules in a switchdev mode state");
+				ret = -ENOTSUP;
+				goto l_end;
+		}
+	}
+
+	if (adapter->is_dev_repr) {
+		if (flow->engine_type != SXE2_FLOW_ENGINE_SWITCH) {
+			PMD_LOG_ERR(DRV,
+				"Failed to config non switch engine rules in representor dev");
+				rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+					"Failed to config non switch engine rules in representor dev");
+				ret = -ENOTSUP;
+				goto l_end;
+		}
+
+		if (sxe2_test_bit(SXE2_FLOW_ACTION_QUEUE, flow->action.act_types) ||
+			sxe2_test_bit(SXE2_FLOW_ACTION_Q_REGION, flow->action.act_types)) {
+			PMD_LOG_ERR(DRV,
+				"Failed to config queue rules in representor dev");
+				rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+					"Failed to config queue rules in representor dev");
+				ret = -ENOTSUP;
+				goto l_end;
+		}
+	}
+
+	if (adapter->switchdev_info.is_switchdev &&
+		adapter->dev_type == SXE2_DEV_T_PF &&
+		!adapter->is_dev_repr &&
+		!adapter->flow_isolated) {
+		if (sxe2_test_bit(SXE2_FLOW_ACTION_TO_VSI, flow->action.act_types)) {
+			if (flow->action.vsi.vsi_index == adapter->vsi_ctxt.dpdk_vsi_id) {
+				PMD_LOG_ERR(DRV,
+					"Failed to config rx fwd rule to current uplink dev");
+					rte_flow_error_set(error, ENOTSUP,
+						RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+						"Failed to config rx fwd rule to current uplink dev");
+					ret = -ENOTSUP;
+					goto l_end;
+			}
+		}
+	}
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_meta_proc(struct rte_eth_dev *dev,
+			       const struct rte_flow_attr *attr,
+			       struct rte_flow *flow_list,
+			       struct rte_flow_error *error)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_flow_list_t *sxe2_flow_list = &flow_list->sxe2_flow_list;
+	struct sxe2_flow *flow = TAILQ_FIRST(sxe2_flow_list);
+	int32_t ret = 0;
+
+	if (attr->priority >= 1) {
+		if (flow->engine_type != SXE2_FLOW_ENGINE_SWITCH) {
+			PMD_LOG_ERR(DRV, "Only support priority 0.");
+			rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+					attr, "Only support priority 0.");
+			ret = -rte_errno;
+			goto l_end;
+		} else if (!adapter->switchdev_info.is_switchdev) {
+			PMD_LOG_ERR(DRV, "Legacy mode only support priority 0.");
+			rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+					attr, "Legacy mode only priority 0.");
+			ret = -rte_errno;
+			goto l_end;
+		} else {
+			flow->meta.flow_prio = attr->priority;
+		}
+	}
+
+	flow->meta.flow_src_vsi = adapter->vsi_ctxt.dpdk_vsi_id;
+
+	if (adapter->is_dev_repr && adapter->repr_priv_data &&
+		adapter->repr_priv_data->parent_adapter) {
+		flow->meta.flow_rule_vsi =
+			adapter->repr_priv_data->parent_adapter->vsi_ctxt.dpdk_vsi_id;
+	} else {
+		flow->meta.flow_rule_vsi = adapter->vsi_ctxt.dpdk_vsi_id;
+	}
+
+	if (flow->engine_type == SXE2_FLOW_ENGINE_SWITCH) {
+		flow->meta.switch_pattern_dup_allow =
+			adapter->devargs.flow_dup_pattern_mode;
+
+		flow->meta.switch_src_direct = SXE2_FLOW_SW_DIRECT_RX;
+
+		if (adapter->switchdev_info.is_switchdev && adapter->is_dev_repr) {
+			flow->meta.switch_src_direct = SXE2_FLOW_SW_DIRECT_TX;
+			flow->meta.flow_src_vsi = adapter->repr_priv_data->repr_vf_vsi_id;
+		}
+	}
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_src_split_proc(struct rte_eth_dev *dev,
+				    struct rte_flow *flow_list,
+				    struct rte_flow_error *error)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_flow_list_t *sxe2_flow_list = &flow_list->sxe2_flow_list;
+	struct sxe2_flow *flow = TAILQ_FIRST(sxe2_flow_list);
+	int32_t ret = 0;
+
+	int32_t idx = 0;
+	uint8_t flow_cnt = 0;
+	uint8_t flow_create_cnt = 0;
+	uint8_t flow_bond_num = 1;
+	uint16_t flow_src_vsi[SXE2_MAX_DRV_TYPE_CNT][SXE2_MAX_BOND_MEMBER_CNT];
+	uint16_t flow_dst_vsi = UINT16_MAX;
+	struct sxe2_flow *flow_new = NULL;
+
+	if (sxe2_test_bit(SXE2_FLOW_ACTION_TO_VSI, flow->action.act_types))
+		flow_dst_vsi = flow->action.vsi.vsi_index;
+
+	for (idx = 0; idx < SXE2_MAX_BOND_MEMBER_CNT; idx++) {
+		flow_src_vsi[SXE2_MAX_DRV_TYPE_DPDK][idx] = UINT16_MAX;
+		flow_src_vsi[SXE2_MAX_DRV_TYPE_KERNEL][idx] = UINT16_MAX;
+	}
+
+	flow_src_vsi[SXE2_MAX_DRV_TYPE_DPDK][0] = adapter->vsi_ctxt.dpdk_vsi_id;
+	flow_src_vsi[SXE2_MAX_DRV_TYPE_KERNEL][0] = adapter->vsi_ctxt.kernel_vsi_id;
+	if (flow->engine_type == SXE2_FLOW_ENGINE_FNAV ||
+		flow->engine_type == SXE2_FLOW_ENGINE_ACL) {
+		if (!adapter->devargs.func_flow_direct_en &&
+			adapter->dev_type != SXE2_DEV_T_PF_BOND) {
+			if (adapter->flow_isolated) {
+				for (idx = 0; idx < flow_bond_num; idx++) {
+					if (flow_src_vsi[SXE2_MAX_DRV_TYPE_KERNEL][idx] !=
+								UINT16_MAX)
+						flow_src_vsi[SXE2_MAX_DRV_TYPE_DPDK][idx] =
+										UINT16_MAX;
+				}
+			} else {
+				for (idx = 0; idx < flow_bond_num; idx++)
+					flow_src_vsi[SXE2_MAX_DRV_TYPE_KERNEL][idx] = UINT16_MAX;
+			}
+		}
+
+		for (idx = 0; idx < flow_bond_num; idx++) {
+			if (flow_dst_vsi == flow_src_vsi[SXE2_MAX_DRV_TYPE_KERNEL][idx])
+				flow_src_vsi[SXE2_MAX_DRV_TYPE_KERNEL][idx] = UINT16_MAX;
+			if (flow_dst_vsi == flow_src_vsi[SXE2_MAX_DRV_TYPE_DPDK][idx])
+				flow_src_vsi[SXE2_MAX_DRV_TYPE_DPDK][idx] = UINT16_MAX;
+		}
+	} else {
+		for (idx = 0; idx < flow_bond_num; idx++)
+			flow_src_vsi[SXE2_MAX_DRV_TYPE_KERNEL][idx] = UINT16_MAX;
+	}
+
+	if (adapter->switchdev_info.is_switchdev && adapter->is_dev_repr) {
+		flow_bond_num = 1;
+		flow_src_vsi[SXE2_MAX_DRV_TYPE_DPDK][0] =
+			adapter->repr_priv_data->repr_vf_u_vsi_id;
+		flow_src_vsi[SXE2_MAX_DRV_TYPE_KERNEL][0] =
+			adapter->repr_priv_data->repr_vf_k_vsi_id;
+	}
+
+	for (idx = 0; idx < flow_bond_num; idx++) {
+		if (flow_src_vsi[SXE2_MAX_DRV_TYPE_KERNEL][idx] != UINT16_MAX)
+			flow_cnt++;
+		if (flow_src_vsi[SXE2_MAX_DRV_TYPE_DPDK][idx] != UINT16_MAX)
+			flow_cnt++;
+	}
+
+	if (flow_cnt == 0) {
+		PMD_LOG_ERR(DRV, "Failed to redirect same device.");
+		rte_flow_error_set(error, ENOTSUP,
+			RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+			"Failed to redirect same device");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	for (idx = 0; idx < flow_bond_num; idx++) {
+		if (flow_src_vsi[SXE2_MAX_DRV_TYPE_DPDK][idx] != UINT16_MAX) {
+			if (flow_create_cnt == 0) {
+				flow->meta.flow_src_vsi =
+					flow_src_vsi[SXE2_MAX_DRV_TYPE_DPDK][idx];
+				flow_create_cnt++;
+			} else {
+				flow_new = rte_zmalloc("sxe2_flow", sizeof(*flow), 0);
+				if (!flow_new) {
+					rte_flow_error_set(error, ENOMEM,
+						   RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+						   "Failed to alloc memory for flow rule");
+					ret = -ENOMEM;
+					goto l_end;
+				}
+				rte_memcpy(flow_new, flow, sizeof(struct sxe2_flow));
+				TAILQ_INSERT_TAIL(sxe2_flow_list, flow_new, next);
+				flow_new->meta.flow_src_vsi =
+						flow_src_vsi[SXE2_MAX_DRV_TYPE_DPDK][idx];
+				flow_create_cnt++;
+			}
+		}
+		if (flow_src_vsi[SXE2_MAX_DRV_TYPE_KERNEL][idx] != UINT16_MAX) {
+			if (flow_create_cnt == 0) {
+				flow->meta.flow_src_vsi =
+					flow_src_vsi[SXE2_MAX_DRV_TYPE_KERNEL][idx];
+				flow_create_cnt++;
+			} else {
+				flow_new = rte_zmalloc("sxe2_flow", sizeof(*flow), 0);
+				if (!flow_new) {
+					rte_flow_error_set(error, ENOMEM,
+						RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+						"Failed to alloc memory for flow rule");
+					ret = -ENOMEM;
+					goto l_end;
+				}
+				rte_memcpy(flow_new, flow, sizeof(struct sxe2_flow));
+				TAILQ_INSERT_TAIL(sxe2_flow_list, flow_new, next);
+				flow_new->meta.flow_src_vsi =
+					flow_src_vsi[SXE2_MAX_DRV_TYPE_KERNEL][idx];
+				flow_create_cnt++;
+			}
+		}
+	}
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_adjust_action(struct rte_eth_dev *dev __rte_unused,
+				   struct rte_flow *flow_list,
+				   struct rte_flow_error *error __rte_unused)
+{
+	struct sxe2_flow_list_t *sxe2_flow_list = &flow_list->sxe2_flow_list;
+	struct sxe2_flow *flow = NULL;
+	int32_t ret = 0;
+	int32_t dest_num = 0;
+	int32_t pass_num = 0;
+	int32_t mark_num = 0;
+	int32_t count_num = 0;
+	int32_t drop_num = 0;
+
+	TAILQ_FOREACH(flow, sxe2_flow_list, next) {
+		if (flow->engine_type == SXE2_FLOW_ENGINE_FNAV) {
+			dest_num = sxe2_test_bit(SXE2_FLOW_ACTION_Q_REGION,
+					    flow->action.act_types) +
+				   sxe2_test_bit(SXE2_FLOW_ACTION_QUEUE,
+					    flow->action.act_types);
+			pass_num = sxe2_test_bit(SXE2_FLOW_ACTION_PASSTHRU,
+					    flow->action.act_types);
+			mark_num = sxe2_test_bit(SXE2_FLOW_ACTION_MARK,
+					    flow->action.act_types);
+			count_num = sxe2_test_bit(SXE2_FLOW_ACTION_COUNT,
+					     flow->action.act_types);
+			drop_num = sxe2_test_bit(SXE2_FLOW_ACTION_DROP,
+					    flow->action.act_types);
+
+			if (dest_num) {
+				sxe2_clear_bit(SXE2_FLOW_ACTION_PASSTHRU,
+					  flow->action.act_types);
+				pass_num = 0;
+				sxe2_clear_bit(SXE2_FLOW_ACTION_TO_VSI,
+					  flow->action.act_types);
+			}
+
+			if (pass_num)
+				flow->action.passthru.vsi_index = flow->meta.flow_src_vsi;
+
+			if (mark_num) {
+				if (dest_num == 0) {
+					flow->action.q_region.q_index = 0;
+					flow->action.q_region.region = 7;
+					flow->action.q_region.vsi_index = flow->meta.flow_src_vsi;
+					sxe2_set_bit(SXE2_FLOW_ACTION_Q_REGION,
+						flow->action.act_types);
+					dest_num++;
+				}
+				sxe2_clear_bit(SXE2_FLOW_ACTION_PASSTHRU,
+					flow->action.act_types);
+				pass_num = 0;
+			}
+			if (count_num) {
+				if (dest_num == 0 && drop_num == 0) {
+					if (pass_num == 0) {
+						sxe2_set_bit(SXE2_FLOW_ACTION_PASSTHRU,
+							flow->action.act_types);
+						flow->action.passthru.vsi_index =
+									flow->meta.flow_src_vsi;
+						pass_num++;
+					}
+				}
+			}
+			PMD_LOG_DEBUG(DRV, "dest_num: %d, pass_num: %d, mark_num: %d, count_num: "
+				"%d, drop_num: %d", dest_num, pass_num, mark_num, count_num,
+				drop_num);
+			PMD_LOG_DEBUG(DRV, "src_vsi: %d", flow->meta.flow_src_vsi);
+		}
+	}
+
+	return ret;
+}
+
+static int32_t sxe2_flow_check_item_empty(uint8_t *item, uint16_t size)
+{
+	uint16_t i = 0;
+
+	for (i = 0; i < size; i++) {
+		if (item[i] != 0)
+			return -1;
+	}
+	return 0;
+}
+
+static int32_t sxe2_flowlist_add_proto_type(struct rte_eth_dev *dev __rte_unused,
+					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);
+	struct sxe2_flow_pattern *pattern = &flow->pattern_outer;
+	int32_t ret = 0;
+
+	if (flow->engine_type == SXE2_FLOW_ENGINE_SWITCH) {
+		if (sxe2_test_bit(SXE2_EXPANSION_OUTER_IPV4, flow->flow_type) &&
+		    sxe2_flow_check_item_empty((uint8_t *)&pattern->item_mask.ipv4,
+			sizeof(pattern->item_mask.ipv4)) == 0) {
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_ETH_TYPE, pattern->map_spec);
+			pattern->item_spec.eth.ether_type =
+				rte_cpu_to_be_16(SXE2_FLOW_ETH_TYPE_IPV4);
+			pattern->item_mask.eth.ether_type = 0xffff;
+		}
+		if (sxe2_test_bit(SXE2_EXPANSION_OUTER_IPV6, flow->flow_type) &&
+		    sxe2_flow_check_item_empty((uint8_t *)&pattern->item_mask.ipv6,
+			sizeof(pattern->item_mask.ipv6)) == 0) {
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_ETH_TYPE, pattern->map_spec);
+			pattern->item_spec.eth.ether_type =
+				rte_cpu_to_be_16(SXE2_FLOW_ETH_TYPE_IPV6);
+			pattern->item_mask.eth.ether_type = 0xffff;
+		}
+
+		if (flow->meta.tunnel_type == SXE2_FLOW_TUNNEL_TYPE_NONE) {
+			if (sxe2_test_bit(SXE2_EXPANSION_OUTER_UDP, flow->flow_type) &&
+			    sxe2_flow_check_item_empty((uint8_t *)&pattern->item_mask.udp,
+				sizeof(pattern->item_mask.udp)) == 0) {
+				if (sxe2_test_bit(SXE2_EXPANSION_OUTER_IPV4,
+					     flow->flow_type)) {
+					sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_PROT, pattern->map_spec);
+					pattern->item_spec.ipv4.protocol =
+						SXE2_FLOW_IP_PROTOCOL_UDP;
+					pattern->item_mask.ipv4.protocol = 0xff;
+				}
+				if (sxe2_test_bit(SXE2_EXPANSION_OUTER_IPV6,
+					     flow->flow_type)) {
+					ret = -EINVAL;
+					rte_flow_error_set(error, ENOENT,
+						RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+						"UDP after IPv6 must has pattern item.");
+					PMD_LOG_ERR(DRV,
+						"UDP after IPv6 must has pattern item.");
+					goto l_end;
+				}
+			}
+			if (sxe2_test_bit(SXE2_EXPANSION_OUTER_TCP, flow->flow_type) &&
+			    sxe2_flow_check_item_empty((uint8_t *)&pattern->item_mask.tcp,
+				sizeof(pattern->item_mask.tcp)) == 0) {
+				if (sxe2_test_bit(SXE2_EXPANSION_OUTER_IPV4,
+					     flow->flow_type)) {
+					sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_PROT,
+						pattern->map_spec);
+					pattern->item_spec.ipv4.protocol =
+						SXE2_FLOW_IP_PROTOCOL_TCP;
+					pattern->item_mask.ipv4.protocol = 0xff;
+				}
+				if (sxe2_test_bit(SXE2_EXPANSION_OUTER_IPV6,
+					     flow->flow_type)) {
+					ret = -EINVAL;
+					rte_flow_error_set(error, ENOENT,
+						RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+						"TCP after IPv6 must has pattern item.");
+					PMD_LOG_ERR(DRV,
+						"TCP after IPv6 must has pattern item.");
+					goto l_end;
+				}
+			}
+			if (sxe2_test_bit(SXE2_EXPANSION_OUTER_SCTP,
+				     flow->flow_type)) {
+				ret = -EINVAL;
+				rte_flow_error_set(error, ENOENT,
+					RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+					"SWITCH not support SCTP.");
+				PMD_LOG_ERR(DRV, "SWITCH not support SCTP.");
+				goto l_end;
+			}
+		}
+	}
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_tunnel_split_proc(struct rte_eth_dev *dev __rte_unused,
+				       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_list_t tunnel_flow_list;
+	struct sxe2_flow *sxe2_flow_exist = NULL;
+	struct sxe2_flow *sxe2_flow_new = NULL;
+	struct sxe2_flow_pattern *pattern = NULL;
+	int32_t ret = 0;
+
+	TAILQ_INIT(&tunnel_flow_list);
+
+	TAILQ_FOREACH(sxe2_flow_exist, sxe2_flow_list, next) {
+		if (sxe2_flow_exist->engine_type != SXE2_FLOW_ENGINE_SWITCH)
+			continue;
+		if (sxe2_test_bit(SXE2_FLOW_FLD_ID_IPV4_PROT,
+				sxe2_flow_exist->pattern_outer.map_spec)) {
+			pattern = &sxe2_flow_exist->pattern_outer;
+			if ((pattern->item_spec.ipv4.protocol &
+				pattern->item_mask.ipv4.protocol) ==
+				(SXE2_FLOW_IP_PROTOCOL_GRE &
+				pattern->item_mask.ipv4.protocol)) {
+				sxe2_flow_new = rte_zmalloc("sxe2_flow",
+					sizeof(struct sxe2_flow), 0);
+				if (!sxe2_flow_new) {
+					rte_flow_error_set(error, ENOMEM,
+						RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+						"Failed to alloc memory for flow rule");
+					ret = -ENOMEM;
+					goto l_end;
+				}
+				rte_memcpy(sxe2_flow_new, sxe2_flow_exist,
+					sizeof(struct sxe2_flow));
+				pattern = &sxe2_flow_new->pattern_outer;
+				sxe2_flow_new->meta.tunnel_type =
+					SXE2_FLOW_TUNNEL_TYPE_GRE;
+				sxe2_set_bit(SXE2_FLOW_HDR_GRE, pattern->hdrs);
+				pattern->item_spec.ipv4.protocol =
+					SXE2_FLOW_IP_PROTOCOL_GRE;
+				pattern->item_mask.ipv4.protocol = 0xff;
+				TAILQ_INSERT_TAIL(&tunnel_flow_list, sxe2_flow_new, next);
+			}
+		}
+	}
+	TAILQ_FOREACH(sxe2_flow_exist, &tunnel_flow_list, next)
+		TAILQ_INSERT_TAIL(sxe2_flow_list, sxe2_flow_exist, next);
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_post_proc(struct rte_eth_dev *dev,
+			       const struct rte_flow_attr *attr,
+			       struct rte_flow *flow_list,
+			       struct rte_flow_error *error)
+{
+	int32_t ret = 0;
+
+	ret = sxe2_flowlist_add_proto_type(dev, flow_list, error);
+	if (ret)
+		goto l_end;
+
+	ret = sxe2_flow_check_function(dev, flow_list, error);
+	if (ret)
+		goto l_end;
+
+	ret = sxe2_flow_meta_proc(dev, attr, flow_list, error);
+	if (ret)
+		goto l_end;
+
+	ret = sxe2_flow_src_split_proc(dev, flow_list, error);
+	if (ret)
+		goto l_end;
+
+	ret = sxe2_flow_adjust_action(dev, flow_list, error);
+	if (ret)
+		goto l_end;
+
+	ret = sxe2_flow_tunnel_split_proc(dev, flow_list, error);
+	if (ret)
+		goto l_end;
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_validate_with_flow(struct rte_eth_dev *dev,
+					struct rte_flow *flow_list,
+					const struct rte_flow_attr *attr,
+					const struct rte_flow_item pattern[],
+					const struct rte_flow_action actions[],
+					struct rte_flow_error *error)
+{
+	int32_t ret = 0;
+	struct sxe2_flow *flow = NULL;
+
+	ret = sxe2_check_para(attr, pattern, actions, error);
+	if (ret != 0)
+		goto l_end;
+
+	ret = sxe2_flow_valid_attr(attr, error);
+	if (ret != 0)
+		goto l_end;
+
+	flow = rte_zmalloc("sxe2_flow", sizeof(*flow), 0);
+	if (!flow) {
+		rte_flow_error_set(error, ENOMEM,
+				RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				"Failed to alloc memory for flow rule");
+		PMD_LOG_ERR(DRV, "Failed to alloc memory for flow rule.");
+		ret = -ENOMEM;
+		goto l_end;
+	}
+
+	TAILQ_INSERT_TAIL(&flow_list->sxe2_flow_list, flow, next);
+	flow->create_err = -1;
+
+	ret = sxe2_flow_parse_pattern(dev, pattern, error, flow);
+	if (ret != 0)
+		goto l_end;
+
+	ret = sxe2_flow_parse_engine(dev, attr, actions, error, flow);
+	if (ret != 0)
+		goto l_end;
+
+	ret = sxe2_flow_parse_action(dev, actions, error, flow);
+	if (ret != 0)
+		goto l_end;
+
+	ret = sxe2_flow_post_proc(dev, attr, flow_list, error);
+	if (ret != 0)
+		goto l_end;
+
+	ret = sxe2_flow_check_flow_list_duplicate(dev, flow_list);
+	if (ret != 0) {
+		rte_flow_error_set(error, EEXIST, RTE_FLOW_ERROR_TYPE_ITEM,
+				NULL, "Duplicate flow.");
+		PMD_LOG_ERR(DRV, "Duplicate flow.");
+		goto l_end;
+	}
+l_end:
+	return ret;
+}
+
+static const char *sxe2_flow_convert_ret_to_flow_msg(int32_t ret)
+{
+	const char *msg = NULL;
+	if (ret > 0)
+		ret = -ret;
+	switch (ret) {
+	case -ENOMEM:
+		msg = "no memory";
+		break;
+	case -ENOTSUP:
+		msg = "not support";
+		break;
+	case -EEXIST:
+		msg = "rule already exist";
+		break;
+	case -ETIMEDOUT:
+		msg = "timeout";
+		break;
+	case -EINVAL:
+		msg = "invalid parameter";
+		break;
+	case -ENOSPC:
+		msg = "no space";
+		break;
+	case -ENOENT:
+		msg = "no such rule";
+		break;
+	default:
+		msg = "unknown error";
+		break;
+	}
+	return msg;
+}
+
+static int32_t sxe2_flow_rte_list_free(struct sxe2_adapter *adapter,
+				   struct rte_flow **flow_ptr,
+				   struct rte_flow_error *error)
+{
+	int32_t ret = 0;
+	int32_t ret1 = 0;
+	struct rte_flow *flow = *flow_ptr;
+	struct rte_flow *flow_temp = NULL;
+	struct sxe2_flow *hw_flow = NULL;
+	struct sxe2_flow *hw_flow_temp = NULL;
+	struct sxe2_fnav_cid_mgr *mgr = NULL;
+	rte_spinlock_lock(&adapter->flow_ctxt.flow_list_lock);
+	TAILQ_FOREACH(flow_temp, &adapter->flow_ctxt.rte_flow_list, next) {
+		if (flow_temp == flow)
+			TAILQ_REMOVE(&adapter->flow_ctxt.rte_flow_list, flow, next);
+	}
+
+	TAILQ_FOREACH_SAFE(hw_flow, &flow->sxe2_flow_list, next, hw_flow_temp) {
+		if (hw_flow->create_err == 0) {
+			if (sxe2_test_bit(SXE2_FLOW_ACTION_COUNT, hw_flow->action.act_types)) {
+				ret = sxe2_flow_query_mgr(adapter, hw_flow, &mgr, error);
+				if (ret) {
+					PMD_LOG_ERR(DRV,
+						"Failed to query flow count, flow id: %u, ret: %d.",
+						hw_flow->flow_id, ret);
+					rte_flow_error_set(error, EIO,
+						RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+						"Failed to query flow count");
+					ret1 = ret;
+				}
+			}
+
+			ret = sxe2_drv_flow_filter_del(adapter, hw_flow);
+			if (ret) {
+				PMD_LOG_ERR(DRV,
+					"Failed to delete flow filter, ret: %d:%s",
+					ret, sxe2_flow_convert_ret_to_flow_msg(ret));
+				rte_flow_error_set(error, EIO,
+					RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+					"Failed to delete flow filter");
+				ret1 = ret;
+			}
+
+			if (sxe2_test_bit(SXE2_FLOW_ACTION_COUNT, hw_flow->action.act_types)) {
+				ret = sxe2_flow_free_mgr(adapter, hw_flow,
+							 &mgr, error);
+				if (ret)
+					ret1 = ret;
+			}
+		}
+
+		TAILQ_REMOVE(&flow->sxe2_flow_list, hw_flow, next);
+		rte_free(hw_flow);
+	}
+	rte_free(flow);
+	*flow_ptr = NULL;
+	rte_spinlock_unlock(&adapter->flow_ctxt.flow_list_lock);
+
+	return ret1;
+}
+
+static int32_t sxe2_flow_validate(struct rte_eth_dev *dev,
+			      const struct rte_flow_attr *attr,
+			      const struct rte_flow_item pattern[],
+			      const struct rte_flow_action actions[],
+			      struct rte_flow_error *error)
+{
+	int32_t ret = 0;
+	struct rte_flow *flow_list = NULL;
+	struct sxe2_flow *hw_flow = NULL;
+	struct sxe2_flow *hw_flow_temp = NULL;
+	flow_list = rte_zmalloc("rte_flow_va", sizeof(*flow_list), 0);
+	if (!flow_list) {
+		rte_flow_error_set(error, ENOMEM,
+				   RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				   "Failed to alloc memory for flow rule");
+		PMD_LOG_ERR(DRV, "Failed to alloc memory for flow rule.");
+		ret = -ENOMEM;
+		goto l_end;
+	}
+	TAILQ_INIT(&flow_list->sxe2_flow_list);
+
+	ret = sxe2_flow_validate_with_flow(dev, flow_list, attr, pattern, actions, error);
+	if (ret != 0)
+		goto l_free;
+l_free:
+
+	TAILQ_FOREACH_SAFE(hw_flow, &flow_list->sxe2_flow_list, next, hw_flow_temp) {
+		TAILQ_REMOVE(&flow_list->sxe2_flow_list, hw_flow, next);
+		rte_free(hw_flow);
+	}
+	rte_free(flow_list);
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_isolate(struct rte_eth_dev *dev,
+				 int32_t enable,
+				 struct rte_flow_error *error)
+{
+	int32_t ret = 0;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	if (dev->data->dev_started) {
+		rte_flow_error_set(error, EBUSY,
+				   RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				   NULL,
+				   "port must be stopped first");
+		ret = -EBUSY;
+		goto l_end;
+	}
+
+	if (adapter->is_dev_repr) {
+		rte_flow_error_set(error, ENOTSUP,
+			RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+			"representor dev cannot change isolated mode ");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (enable == adapter->flow_isolated)
+		goto l_end;
+
+	if (adapter->dev_type == SXE2_DEV_T_VF &&
+		adapter->switchdev_info.is_switchdev) {
+		rte_flow_error_set(error, ENOTSUP,
+			RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+			"isolated mode cannot be change when port in switch dev mode");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	rte_spinlock_lock(&adapter->flow_ctxt.flow_list_lock);
+	if (!TAILQ_EMPTY(&adapter->flow_ctxt.rte_flow_list))
+		PMD_DEV_LOG_WARN(adapter, DRV,
+			"The configured flow item may not take effect.");
+	rte_spinlock_unlock(&adapter->flow_ctxt.flow_list_lock);
+
+	adapter->flow_isolated = !!enable;
+
+	ret = sxe2_l2_rule_update(adapter);
+	if (ret != 0)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to update l2 rule");
+
+	ret = sxe2_switchdev_rule_update(adapter);
+	if (ret != 0)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to update switchdev rule");
+
+l_end:
+	if (ret == 0)
+		adapter->flow_isolate_cfg = !!enable;
+	return ret;
+}
+
+static struct rte_flow *sxe2_flow_create(struct rte_eth_dev *dev,
+					 const struct rte_flow_attr *attr,
+					 const struct rte_flow_item pattern[],
+					 const struct rte_flow_action action[],
+					 struct rte_flow_error *error)
+{
+	int32_t ret = 0;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct rte_flow *flow_list = NULL;
+	struct sxe2_flow *flow = NULL;
+
+	flow_list = rte_zmalloc("sxe2_flow_create", sizeof(*flow_list), 0);
+	if (!flow_list) {
+		rte_flow_error_set(error, ENOMEM,
+				   RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				   "Failed to alloc memory for flow rule");
+		PMD_LOG_ERR(DRV, "Failed to alloc memory for flow rule.");
+		ret = -ENOMEM;
+		goto l_end;
+	}
+	TAILQ_INIT(&flow_list->sxe2_flow_list);
+
+	ret = sxe2_flow_validate_with_flow(dev, flow_list, attr, pattern, action, error);
+	if (ret != 0)
+		goto l_free_flow;
+
+	TAILQ_FOREACH(flow, &flow_list->sxe2_flow_list, next) {
+		ret = sxe2_fnav_get_filter_cid(adapter, flow);
+		if (ret != 0) {
+			PMD_LOG_ERR(DRV, "fnav get stats id failed, ret:%d", ret);
+			rte_flow_error_set(error, EIO,
+				   RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				   "Failed to add fnav rule:alloc cid failed.");
+			goto l_free_flow;
+		}
+		ret = sxe2_drv_flow_filter_add(adapter, flow);
+		if (ret) {
+			rte_flow_error_set(error, EINVAL,
+				   RTE_FLOW_ERROR_TYPE_ITEM, NULL,
+				   "Failed to add flow filter to hw.");
+			PMD_LOG_ERR(DRV, "Failed to add flow filter to hw.ret:%d:%s",
+				ret, sxe2_flow_convert_ret_to_flow_msg(ret));
+			goto l_free_flow;
+		}
+	}
+
+	TAILQ_INSERT_TAIL(&adapter->flow_ctxt.rte_flow_list, flow_list, next);
+	goto l_end;
+l_free_flow:
+	(void)sxe2_flow_rte_list_free(adapter, &flow_list, error);
+l_end:
+	return flow_list;
+}
+
+static int32_t sxe2_flow_destroy(struct rte_eth_dev *dev,
+			     struct rte_flow *flow,
+			     struct rte_flow_error *error)
+{
+	int32_t ret = 0;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	ret = sxe2_flow_rte_list_free(adapter, &flow, error);
+	if (ret)
+		PMD_LOG_ERR(DRV, "Failed to destroy flow.ret:%d.", ret);
+	return ret;
+}
+
+static int32_t sxe2_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
+{
+	int32_t ret = 0;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct rte_flow *flow = NULL;
+	struct rte_flow *tmp_flow = NULL;
+	struct rte_flow_list_t *flow_list = &adapter->flow_ctxt.rte_flow_list;
+	TAILQ_FOREACH_SAFE(flow, flow_list, next, tmp_flow) {
+		ret = sxe2_flow_destroy(dev, flow, error);
+		if (ret) {
+			PMD_LOG_ERR(DRV, "Failed to flush flows.ret:%d.", ret);
+
+			if (ret != -EAGAIN)
+				ret = -EINVAL;
+			goto l_end;
+		}
+	}
+l_end:
+	return ret;
+}
+
+int32_t sxe2_fnav_get_filter_cid(struct sxe2_adapter *adapter, struct sxe2_flow *flow)
+{
+	int32_t ret = 0;
+	struct sxe2_fnav_cid_mgr_list_t *cid_mgr_list =
+				&adapter->flow_ctxt.hw_res.fnav_cid_mgr_list;
+	uint32_t stat_index;
+	uint32_t user_id;
+	uint32_t driver_id;
+	struct sxe2_fnav_cid_mgr *temp = NULL;
+	struct sxe2_fnav_cid_mgr *mgr = NULL;
+
+	if (sxe2_test_bit(SXE2_FLOW_ACTION_COUNT, flow->action.act_types)) {
+		user_id = flow->action.count.user_id;
+		driver_id = flow->action.count.driver_id;
+
+		TAILQ_FOREACH(temp, cid_mgr_list, next) {
+			if (temp->user_id == user_id &&
+				temp->driver_id == driver_id) {
+				mgr = temp;
+				break;
+			}
+		}
+		if (mgr == NULL) {
+			mgr = rte_zmalloc("sxe2_fnav_cid_mgr",
+				sizeof(struct sxe2_fnav_cid_mgr), 0);
+			if (!mgr) {
+				PMD_LOG_ERR(DRV,
+					"Failed to alloc sxe2vf_fnav_cid_mgr memory.");
+				ret = -ENOMEM;
+				goto l_end;
+			}
+
+			ret = sxe2_drv_flow_fnav_get_stat_id(adapter, &stat_index);
+			if (ret) {
+				PMD_LOG_ERR(DRV, "Failed to alloc fw count id.");
+				rte_free(mgr);
+				goto l_end;
+			}
+
+			TAILQ_INSERT_TAIL(cid_mgr_list, mgr, next);
+			mgr->user_id = user_id;
+			mgr->driver_id = driver_id;
+			mgr->stat_index = stat_index;
+			mgr->count_type = adapter->flow_ctxt.hw_res.count_type;
+		}
+		flow->action.count.stat_index = mgr->stat_index;
+		flow->action.count.stat_ctrl = mgr->count_type;
+	}
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_flow_free_mgr(struct sxe2_adapter *adapter,
+		       struct sxe2_flow *flow,
+		       struct sxe2_fnav_cid_mgr **mgr_ptr,
+		       struct rte_flow_error *error)
+{
+	int32_t ret = 0;
+	struct sxe2_fnav_cid_mgr_list_t *cid_mgr_list =
+				&adapter->flow_ctxt.hw_res.fnav_cid_mgr_list;
+	struct sxe2_fnav_cid_mgr *mgr = *mgr_ptr;
+	uint32_t user_id = flow->action.count.user_id;
+	if (user_id == 0) {
+		TAILQ_REMOVE(cid_mgr_list, mgr, next);
+		ret = sxe2_drv_flow_fnav_free_stat(adapter, mgr->stat_index);
+		if (ret) {
+			rte_flow_error_set(error, EIO,
+				RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+				"Failed to free flow count.");
+			PMD_LOG_ERR(DRV,
+				"Failed to free flow count, flow id: %u, ret: %d.",
+				flow->flow_id, ret);
+		}
+		rte_free(mgr);
+		*mgr_ptr = NULL;
+	}
+	return ret;
+}
+
+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 ret = 0;
+	struct sxe2_fnav_cid_mgr_list_t *cid_mgr_list =
+				&adapter->flow_ctxt.hw_res.fnav_cid_mgr_list;
+	struct sxe2_fnav_cid_mgr *temp = NULL;
+	struct sxe2_fnav_cid_mgr *mgr = NULL;
+	uint32_t user_id = flow->action.count.user_id;
+	uint32_t driver_id = flow->action.count.driver_id;
+
+	TAILQ_FOREACH(temp, cid_mgr_list, next) {
+		if (temp->user_id == user_id &&
+			temp->driver_id == driver_id) {
+			mgr = temp;
+			break;
+		}
+	}
+	if (!mgr) {
+		rte_flow_error_set(error, EINVAL,
+			RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+			"fnav flow query count invalid user_id or driver_id.");
+		PMD_LOG_ERR(DRV,
+			"fnav flow query count invalid user_id or driver_id.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+	ret = sxe2_drv_flow_fnav_query_stat(adapter, mgr);
+	if (ret) {
+		rte_flow_error_set(error, EINVAL,
+			RTE_FLOW_ERROR_TYPE_ITEM, NULL,
+			"Failed to query flow count.");
+		PMD_LOG_ERR(DRV,
+			"Failed to query flow count, flow id: %u, ret: %d.",
+			flow->flow_id, ret);
+		goto l_end;
+	}
+	*mgr_ptr = mgr;
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_query_count(struct sxe2_adapter *adapter,
+				 struct sxe2_flow *flow,
+				 struct rte_flow_query_count *count,
+				 struct rte_flow_error *error)
+{
+	int32_t ret = 0;
+	struct sxe2_fnav_cid_mgr *mgr = NULL;
+	switch (flow->action.count.stat_ctrl) {
+	case SXE2_FNAV_STAT_ENA_NONE:
+		count->hits_set = 0;
+		count->bytes_set = 0;
+		break;
+	case SXE2_FNAV_STAT_ENA_PKTS:
+		count->hits_set = 1;
+		count->bytes_set = 0;
+		break;
+	case SXE2_FNAV_STAT_ENA_BYTES:
+		count->hits_set = 0;
+		count->bytes_set = 1;
+		break;
+	case SXE2_FNAV_STAT_ENA_ALL:
+		count->hits_set = 1;
+		count->bytes_set = 1;
+		break;
+	default:
+		ret = -EINVAL;
+		goto l_end;
+	}
+	if (!sxe2_test_bit(SXE2_FLOW_ACTION_COUNT, flow->action.act_types)) {
+		rte_flow_error_set(error, EINVAL,
+			RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+			"this flow don't have count action.");
+		PMD_LOG_ERR(DRV, "this flow don't have count action.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+	ret = sxe2_flow_query_mgr(adapter, flow, &mgr, error);
+	if (ret) {
+		PMD_LOG_ERR(DRV,
+			"Failed to query flow count, flow id: %u, ret: %d.",
+			flow->flow_id, ret);
+		goto l_end;
+	}
+	count->hits = mgr->hits;
+	count->bytes = mgr->bytes;
+	if (count->reset) {
+		mgr->hits = 0;
+		mgr->bytes = 0;
+	}
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_query(struct rte_eth_dev *dev,
+			   struct rte_flow *flow_list,
+			   const struct rte_flow_action *actions,
+			   void *data,
+			   struct rte_flow_error *error)
+{
+	int32_t ret = 0;
+	struct rte_flow_query_count *count = data;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_flow *flow = NULL;
+
+	if (!flow_list) {
+		ret = -EINVAL;
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Invalid flow");
+		PMD_LOG_ERR(DRV, "Invalid flow to query flow.");
+		goto l_end;
+	}
+
+	rte_spinlock_lock(&adapter->flow_ctxt.flow_list_lock);
+	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
+		switch (actions->type) {
+		case RTE_FLOW_ACTION_TYPE_VOID:
+			break;
+		case RTE_FLOW_ACTION_TYPE_COUNT:
+			flow = TAILQ_FIRST(&flow_list->sxe2_flow_list);
+			ret = sxe2_flow_query_count(adapter, flow, count, error);
+			if (ret) {
+				PMD_LOG_ERR(DRV,
+					"Failed to query flow count, flow id: %u, ret: %d.",
+					flow->flow_id, ret);
+				goto l_end_unlock;
+			}
+			break;
+		default:
+			rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_ACTION,
+					actions,
+					"action not supported");
+			PMD_LOG_ERR(DRV,
+					"Failed to query flow action type:%d.",
+					actions->type);
+			ret = -ENOTSUP;
+			goto l_end_unlock;
+		}
+	}
+
+l_end_unlock:
+	rte_spinlock_unlock(&adapter->flow_ctxt.flow_list_lock);
+
+l_end:
+	return ret;
+}
+
+const struct rte_flow_ops sxe2_flow_ops = {
+	.validate = sxe2_flow_validate,
+	.create = sxe2_flow_create,
+	.destroy = sxe2_flow_destroy,
+	.flush = sxe2_flow_flush,
+	.query = sxe2_flow_query,
+	.isolate = sxe2_flow_isolate,
+};
+
+int32_t sxe2_flow_ops_get(struct rte_eth_dev *dev, const struct rte_flow_ops **ops)
+{
+	int32_t ret = 0;
+
+	if (dev == NULL) {
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	*ops = &sxe2_flow_ops;
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_flow_init(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+	TAILQ_INIT(&adapter->flow_ctxt.rte_flow_list);
+	TAILQ_INIT(&adapter->flow_ctxt.hw_res.fnav_cid_mgr_list);
+	if (adapter->devargs.fnav_stat_type)
+		adapter->flow_ctxt.hw_res.count_type =
+			adapter->devargs.fnav_stat_type;
+	else
+		adapter->flow_ctxt.hw_res.count_type = SXE2_FNAV_STAT_ENA_ALL;
+
+	adapter->flow_ctxt.fnav_inited = 1;
+	rte_spinlock_init(&adapter->flow_ctxt.flow_list_lock);
+	return ret;
+}
+
+int32_t sxe2_flow_uninit(struct rte_eth_dev *dev)
+{
+	int32_t ret = 0;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct rte_flow_error error;
+	struct sxe2_fnav_cid_mgr *mgr = NULL;
+	struct sxe2_fnav_cid_mgr *temp = NULL;
+	struct sxe2_fnav_cid_mgr_list_t *cid_mgr_list =
+						&adapter->flow_ctxt.hw_res.fnav_cid_mgr_list;
+
+	ret = sxe2_flow_flush(dev, &error);
+	if (ret)
+		PMD_LOG_ERR(DRV, "Failed to flush flow, ret: %d.", ret);
+
+	TAILQ_FOREACH_SAFE(mgr, cid_mgr_list, next, temp) {
+		TAILQ_REMOVE(cid_mgr_list, mgr, next);
+		ret = sxe2_drv_flow_fnav_free_stat(adapter, mgr->stat_index);
+		if (ret)
+			PMD_LOG_ERR(DRV,
+				"Failed to free fnav stat id, ret: %d.", ret);
+		rte_free(mgr);
+	}
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_flow.h b/drivers/net/sxe2/sxe2_flow.h
new file mode 100644
index 0000000000..9970fddcf0
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_flow.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_FLOW_H__
+#define __SXE2_FLOW_H__
+#include <rte_flow_driver.h>
+#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);
+
+int32_t sxe2_flow_uninit(struct rte_eth_dev *dev);
+
+int32_t sxe2_fnav_get_filter_cid(struct sxe2_adapter *adapter, struct sxe2_flow *flow);
+
+int32_t sxe2_flow_free_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_query_mgr(struct sxe2_adapter *adapter,
+			struct sxe2_flow *flow,
+			struct sxe2_fnav_cid_mgr **mgr_ptr,
+			struct rte_flow_error *error);
+#endif /* __SXE2_FLOW_H__ */
diff --git a/drivers/net/sxe2/sxe2_flow_parse_action.c b/drivers/net/sxe2/sxe2_flow_parse_action.c
new file mode 100644
index 0000000000..a9559e2d7e
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_flow_parse_action.c
@@ -0,0 +1,1182 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#include "sxe2_flow_parse_action.h"
+#include "sxe2_common_log.h"
+#include "sxe2_flow_public.h"
+#include "sxe2_ethdev.h"
+#include "sxe2_vsi.h"
+
+
+static int32_t sxe2_flow_check_rss_action_attr(const struct rte_flow_action_rss *rss,
+					   struct rte_flow_error *error)
+{
+	int32_t ret = ENOTSUP;
+	switch (rss->func) {
+	case RTE_ETH_HASH_FUNCTION_DEFAULT:
+	case RTE_ETH_HASH_FUNCTION_TOEPLITZ:
+	case RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ:
+	case RTE_ETH_HASH_FUNCTION_SIMPLE_XOR:
+		break;
+	default:
+		PMD_LOG_ERR(DRV, "RSS hash function[%d] not support.", rss->func);
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (rss->level > 2)
+		rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+			"RSS  level is could not be greater than 2");
+	if (rss->key_len)
+		rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+			"a nonzero RSS key_len is not supported");
+	if (rss->queue_num)
+		rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+			"a non-NULL RSS queue is not supported");
+	ret = 0;
+l_end:
+	return ret;
+}
+
+
+static int32_t sxe2_flow_set_rss_action_func(enum rte_eth_hash_function rss_func,
+					 uint64_t rss_type,
+					 struct sxe2_flow *flow,
+					 struct rte_flow_error *error)
+{
+	int32_t ret = 0;
+	if (rss_func == RTE_ETH_HASH_FUNCTION_SIMPLE_XOR) {
+		if (flow->has_hdr) {
+			ret = -EINVAL;
+			rte_flow_error_set(error, -EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+				"Failed to cfg Simple XOR hash with not empty pattern");
+			PMD_LOG_ERR(DRV, "Failed to cfg Simple XOR hash with not empty pattern.");
+			goto l_end;
+		}
+	} else {
+		if (!flow->has_hdr) {
+			ret = -EINVAL;
+			rte_flow_error_set(error, -EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+				"Failed to cfg Simple hash with empty pattern");
+			PMD_LOG_ERR(DRV, "Failed to cfg Simple hash with empty pattern.");
+			goto l_end;
+		}
+	}
+
+	if (rss_func == RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ) {
+		if (rss_type & (RTE_ETH_RSS_L3_SRC_ONLY |
+					RTE_ETH_RSS_L3_DST_ONLY |
+					RTE_ETH_RSS_L4_SRC_ONLY |
+					RTE_ETH_RSS_L4_DST_ONLY)) {
+			ret = -EINVAL;
+			rte_flow_error_set(error, -EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+				"Failed to cfg symm func rss_type l3/l4 only.");
+			PMD_LOG_ERR(DRV, "Failed to cfg symm func rss_type l3/l4 only.");
+			goto l_end;
+		}
+
+		if (!(rss_type & (RTE_ETH_RSS_IPV4 | RTE_ETH_RSS_IPV6 |
+					RTE_ETH_RSS_FRAG_IPV4 | RTE_ETH_RSS_FRAG_IPV6 |
+					RTE_ETH_RSS_NONFRAG_IPV4_UDP |
+					RTE_ETH_RSS_NONFRAG_IPV6_UDP |
+					RTE_ETH_RSS_NONFRAG_IPV4_TCP |
+					RTE_ETH_RSS_NONFRAG_IPV6_TCP |
+					RTE_ETH_RSS_NONFRAG_IPV4_SCTP |
+					RTE_ETH_RSS_NONFRAG_IPV6_SCTP))) {
+			ret = -EINVAL;
+			rte_flow_error_set(error, -EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+				"Failed to cfg symm func unsupported rss_type.");
+			PMD_LOG_ERR(DRV, "Failed to cfg symm func unsupported rss_type.");
+			goto l_end;
+		}
+		flow->action.rss.func = SXE2_RSS_HASH_FUNC_SYM_TOEPLITZ;
+	}
+	if (rss_func == RTE_ETH_HASH_FUNCTION_SIMPLE_XOR)
+		flow->action.rss.func = SXE2_RSS_HASH_FUNC_XOR;
+	if (rss_func == RTE_ETH_HASH_FUNCTION_DEFAULT)
+		flow->action.rss.func = SXE2_RSS_HASH_FUNC_TOEPLITZ;
+	if (rss_func == RTE_ETH_HASH_FUNCTION_TOEPLITZ)
+		flow->action.rss.func = SXE2_RSS_HASH_FUNC_TOEPLITZ;
+l_end:
+	return ret;
+}
+
+
+static uint64_t sxe2_hash_invalid_comb[] = {
+	RTE_ETH_RSS_IPV4 | RTE_ETH_RSS_NONFRAG_IPV4_UDP,
+	RTE_ETH_RSS_IPV4 | RTE_ETH_RSS_NONFRAG_IPV4_TCP,
+	RTE_ETH_RSS_IPV4 | RTE_ETH_RSS_NONFRAG_IPV4_SCTP,
+	RTE_ETH_RSS_IPV6 | RTE_ETH_RSS_NONFRAG_IPV6_UDP,
+	RTE_ETH_RSS_IPV6 | RTE_ETH_RSS_NONFRAG_IPV6_TCP,
+	RTE_ETH_RSS_IPV6 | RTE_ETH_RSS_NONFRAG_IPV6_SCTP,
+	RTE_ETH_RSS_FRAG_IPV4 | RTE_ETH_RSS_NONFRAG_IPV4_OTHER,
+	RTE_ETH_RSS_FRAG_IPV6 | RTE_ETH_RSS_NONFRAG_IPV6_OTHER,
+	RTE_ETH_RSS_L3_PRE32 | RTE_ETH_RSS_L3_PRE48 | RTE_ETH_RSS_L3_PRE64,
+};
+
+struct sxe2_rss_attr_type {
+	uint64_t attr;
+	uint64_t type;
+};
+
+static struct sxe2_rss_attr_type sxe2_rss_attr_valid_type[] = {
+	{RTE_ETH_RSS_L2_SRC_ONLY | RTE_ETH_RSS_L2_DST_ONLY, RTE_ETH_RSS_ETH},
+	{RTE_ETH_RSS_L3_SRC_ONLY | RTE_ETH_RSS_L3_DST_ONLY, SXE2_VALID_RSS_L3},
+	{RTE_ETH_RSS_L4_SRC_ONLY | RTE_ETH_RSS_L4_DST_ONLY, SXE2_VALID_RSS_L4},
+
+	{RTE_ETH_RSS_L3_PRE32, SXE2_VALID_RSS_IPV6},
+	{RTE_ETH_RSS_L3_PRE48, SXE2_VALID_RSS_IPV6},
+	{RTE_ETH_RSS_L3_PRE64, SXE2_VALID_RSS_IPV6},
+	{SXE2_INVALID_RSS_ATTR, 0}
+};
+
+
+static void sxe2_flow_action_pre(struct sxe2_flow *flow)
+{
+	flow->action.vsi.vsi_index = UINT16_MAX;
+	flow->action.vsi_list.vsi_cnt = 0;
+	sxe2_bitmap_zero(flow->action.vsi_list.vsi_list_map, SXE2_VSI_MAX);
+}
+
+static void sxe2_flow_action_post(struct sxe2_flow *flow, uint8_t action_num[])
+{
+	if (sxe2_test_bit(SXE2_FLOW_ACTION_TO_VSI, flow->action.act_types))
+		action_num[SXE2_FLOW_ACTION_TO_VSI] = 1;
+
+	if (sxe2_test_bit(SXE2_FLOW_ACTION_TO_VSI_LIST, flow->action.act_types))
+		action_num[SXE2_FLOW_ACTION_TO_VSI_LIST] = 1;
+}
+
+
+static void sxe2_flow_action_vsi_merge(struct sxe2_flow *flow, uint16_t add_vsi_id)
+{
+	if (flow->action.vsi_list.vsi_cnt == 0) {
+		if (flow->action.vsi.vsi_index == UINT16_MAX) {
+			flow->action.vsi.vsi_index = add_vsi_id;
+			sxe2_set_bit(SXE2_FLOW_ACTION_TO_VSI, flow->action.act_types);
+			goto l_end;
+		}
+
+		if (flow->action.vsi.vsi_index == add_vsi_id)
+			goto l_end;
+
+		sxe2_set_bit(flow->action.vsi.vsi_index, flow->action.vsi_list.vsi_list_map);
+		sxe2_set_bit(add_vsi_id, flow->action.vsi_list.vsi_list_map);
+		flow->action.vsi_list.vsi_cnt = 2;
+		flow->action.vsi.vsi_index = UINT16_MAX;
+		sxe2_clear_bit(SXE2_FLOW_ACTION_TO_VSI, flow->action.act_types);
+		sxe2_set_bit(SXE2_FLOW_ACTION_TO_VSI_LIST, flow->action.act_types);
+	}
+
+	if (sxe2_test_bit(add_vsi_id, flow->action.vsi_list.vsi_list_map))
+		goto l_end;
+
+	sxe2_set_bit(add_vsi_id, flow->action.vsi_list.vsi_list_map);
+	flow->action.vsi_list.vsi_cnt++;
+
+l_end:
+	return;
+}
+
+
+static int32_t sxe2_flow_vsi_get_ethdev(struct rte_eth_dev *dev,
+				uint16_t dev_port_id, uint16_t *vsi_index)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct rte_eth_dev *dst_dev;
+	struct sxe2_adapter *dst_adapter;
+	int32_t ret = 0;
+
+	dst_dev = &rte_eth_devices[dev_port_id];
+	if (!dst_dev->data) {
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (!sxe2_ethdev_check(dst_dev)) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "dst dev is not sxe2 ethdev.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	dst_adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dst_dev);
+	if (!dst_adapter) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "dst dev adapter is null.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (adapter->dev_info.pci.serial_number != dst_adapter->dev_info.pci.serial_number) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "dst dev sn is miss match current dev.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (adapter->dev_type != SXE2_DEV_T_PF_BOND) {
+		if (adapter->pf_idx != dst_adapter->pf_idx) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "dst dev pf id is miss match current dev.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+	}
+
+	if (dst_adapter->is_dev_repr) {
+		if (dst_adapter->repr_priv_data == NULL) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "dst dev repr data is null.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+		*vsi_index = dst_adapter->repr_priv_data->repr_vf_vsi_id;
+	} else {
+		*vsi_index = dst_adapter->vsi_ctxt.dpdk_vsi_id;
+	}
+
+l_end:
+	return ret;
+}
+static int32_t sxe2_flow_check_rss_action_type_with_pattern(struct sxe2_flow_pattern *pattern,
+							uint64_t rss_type)
+{
+	uint64_t rss_type_allow = pattern->rss_type_allow;
+	int32_t ret = -EINVAL;
+
+	if ((rss_type & rss_type_allow) != rss_type)
+		goto l_end;
+
+	if (sxe2_test_bit(SXE2_FLOW_HDR_VLAN, pattern->hdrs) &&
+		!sxe2_test_bit(SXE2_FLOW_HDR_QINQ, pattern->hdrs)) {
+		if ((rss_type & RTE_ETH_RSS_C_VLAN) != 0 &&
+			(rss_type & RTE_ETH_RSS_S_VLAN) == 0)
+			goto l_end;
+	}
+	ret = 0;
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_check_rss_action_type_valid(uint64_t rss_type)
+{
+	struct sxe2_rss_attr_type *attr_type;
+	uint32_t i;
+	int32_t ret = -EINVAL;
+
+	for (i = 0; i < RTE_DIM(sxe2_hash_invalid_comb); i++) {
+		if (rte_popcount64(rss_type & sxe2_hash_invalid_comb[i]) > 1) {
+			PMD_LOG_ERR(DRV, "Error rss_type invalid comb[%d].", i);
+			goto l_end;
+		}
+	}
+
+	for (i = 0; i < RTE_DIM(sxe2_rss_attr_valid_type); i++) {
+		attr_type = &sxe2_rss_attr_valid_type[i];
+		if ((attr_type->attr & rss_type) &&
+				!(attr_type->type & rss_type)) {
+			PMD_LOG_ERR(DRV, "Rss_type valid_comb[%d] check error.", i);
+			goto l_end;
+		}
+	}
+
+	ret = 0;
+l_end:
+	return ret;
+}
+
+
+static void sxe2_flow_set_rss_action_type_l234(BITMAP_TYPE *hdr,
+					       BITMAP_TYPE *fld,
+					       uint64_t rss_type)
+{
+	if (rss_type & RTE_ETH_RSS_ETH) {
+		if (rss_type & RTE_ETH_RSS_L2_SRC_ONLY) {
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_ETH_SA, fld);
+		} else if (rss_type & RTE_ETH_RSS_L2_DST_ONLY) {
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_ETH_DA, fld);
+		} else {
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_ETH_SA, fld);
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_ETH_DA, fld);
+		}
+	}
+	if (rss_type & RTE_ETH_RSS_L2_PAYLOAD) {
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_ETH_TYPE, fld);
+		sxe2_set_bit(SXE2_FLOW_HDR_ETH_NON_IP, hdr);
+	}
+
+	if (sxe2_test_bit(SXE2_FLOW_HDR_VLAN, hdr)) {
+		if (rss_type & RTE_ETH_RSS_S_VLAN)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_S_TCI, fld);
+	}
+	if (sxe2_test_bit(SXE2_FLOW_HDR_QINQ, hdr)) {
+		if (rss_type & RTE_ETH_RSS_C_VLAN)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_C_TCI, fld);
+	}
+
+	if (rss_type & (RTE_ETH_RSS_IPV4 |
+				RTE_ETH_RSS_NONFRAG_IPV4_OTHER |
+				RTE_ETH_RSS_NONFRAG_IPV4_UDP |
+				RTE_ETH_RSS_NONFRAG_IPV4_TCP |
+				RTE_ETH_RSS_NONFRAG_IPV4_SCTP |
+				RTE_ETH_RSS_FRAG_IPV4 |
+				RTE_ETH_RSS_IPV4_CHKSUM)) {
+		if (rss_type & RTE_ETH_RSS_L3_SRC_ONLY) {
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_SA, fld);
+		} else if (rss_type & RTE_ETH_RSS_L3_DST_ONLY) {
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_DA, fld);
+		} else {
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_SA, fld);
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_DA, fld);
+		}
+
+		if (rss_type & RTE_ETH_RSS_NONFRAG_IPV4_OTHER)
+			sxe2_set_bit(SXE2_FLOW_HDR_IPV_OTHER, hdr);
+		if (rss_type & RTE_ETH_RSS_FRAG_IPV4) {
+			sxe2_set_bit(SXE2_FLOW_HDR_IPV_FRAG, hdr);
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_ID, fld);
+		}
+
+		if (rss_type & RTE_ETH_RSS_IPV4_CHKSUM)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_CHKSUM, fld);
+	}
+
+	if (rss_type & (RTE_ETH_RSS_IPV6 |
+				RTE_ETH_RSS_FRAG_IPV6 |
+				RTE_ETH_RSS_NONFRAG_IPV6_OTHER |
+				RTE_ETH_RSS_NONFRAG_IPV6_UDP |
+				RTE_ETH_RSS_NONFRAG_IPV6_TCP |
+				RTE_ETH_RSS_NONFRAG_IPV6_SCTP)) {
+		if (rss_type & RTE_ETH_RSS_L3_PRE32) {
+			if (rss_type & RTE_ETH_RSS_L3_SRC_ONLY) {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_PRE32_SA, fld);
+			} else if (rss_type & RTE_ETH_RSS_L3_DST_ONLY) {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_PRE32_DA, fld);
+			} else {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_PRE32_SA, fld);
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_PRE32_DA, fld);
+			}
+		} else if (rss_type & RTE_ETH_RSS_L3_PRE48) {
+			if (rss_type & RTE_ETH_RSS_L3_SRC_ONLY) {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_PRE48_SA, fld);
+			} else if (rss_type & RTE_ETH_RSS_L3_DST_ONLY) {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_PRE48_DA, fld);
+			} else {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_PRE48_SA, fld);
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_PRE48_DA, fld);
+			}
+		} else if (rss_type & RTE_ETH_RSS_L3_PRE64) {
+			if (rss_type & RTE_ETH_RSS_L3_SRC_ONLY) {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_PRE64_SA, fld);
+			} else if (rss_type & RTE_ETH_RSS_L3_DST_ONLY) {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_PRE64_DA, fld);
+			} else {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_PRE64_SA, fld);
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_PRE64_DA, fld);
+			}
+		} else {
+			if (rss_type & RTE_ETH_RSS_L3_SRC_ONLY) {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_SA, fld);
+			} else if (rss_type & RTE_ETH_RSS_L3_DST_ONLY) {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_DA, fld);
+			} else {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_SA, fld);
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_DA, fld);
+			}
+		}
+		if (rss_type & RTE_ETH_RSS_FRAG_IPV6) {
+			sxe2_set_bit(SXE2_FLOW_HDR_IPV_FRAG, hdr);
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_ID, fld);
+		}
+		if (rss_type & RTE_ETH_RSS_NONFRAG_IPV6_OTHER)
+			sxe2_set_bit(SXE2_FLOW_HDR_IPV_OTHER, hdr);
+	}
+
+	if (rss_type & (RTE_ETH_RSS_NONFRAG_IPV4_TCP |
+				RTE_ETH_RSS_NONFRAG_IPV6_TCP)) {
+		if (rss_type & RTE_ETH_RSS_L4_SRC_ONLY) {
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_TCP_SRC_PORT, fld);
+		} else if (rss_type & RTE_ETH_RSS_L4_DST_ONLY) {
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_TCP_DST_PORT, fld);
+		} else {
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_TCP_SRC_PORT, fld);
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_TCP_DST_PORT, fld);
+		}
+		if (rss_type & RTE_ETH_RSS_L4_CHKSUM)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_TCP_CHKSUM, fld);
+	}
+
+	if (rss_type & (RTE_ETH_RSS_NONFRAG_IPV4_UDP |
+				RTE_ETH_RSS_NONFRAG_IPV6_UDP)) {
+		if (rss_type & RTE_ETH_RSS_L4_SRC_ONLY) {
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_UDP_SRC_PORT, fld);
+		} else if (rss_type & RTE_ETH_RSS_L4_DST_ONLY) {
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_UDP_DST_PORT, fld);
+		} else {
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_UDP_SRC_PORT, fld);
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_UDP_DST_PORT, fld);
+		}
+		if (rss_type & RTE_ETH_RSS_L4_CHKSUM)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_UDP_CHKSUM, fld);
+	}
+
+	if (rss_type & (RTE_ETH_RSS_NONFRAG_IPV4_SCTP |
+				RTE_ETH_RSS_NONFRAG_IPV6_SCTP)) {
+		if (rss_type & RTE_ETH_RSS_L4_SRC_ONLY) {
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_SCTP_SRC_PORT, fld);
+		} else if (rss_type & RTE_ETH_RSS_L4_DST_ONLY) {
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_SCTP_DST_PORT, fld);
+		} else {
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_SCTP_SRC_PORT, fld);
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_SCTP_DST_PORT, fld);
+		}
+		if (rss_type & RTE_ETH_RSS_L4_CHKSUM)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_SCTP_CHKSUM, fld);
+	}
+}
+
+
+static int32_t sxe2_flow_set_rss_action_hdr_type(struct sxe2_flow *flow,
+		bool is_inner)
+{
+	struct sxe2_flow_action_rss *rss = &flow->action.rss;
+	BITMAP_TYPE *hdr = rss->hdr_out;
+	int32_t ret = 0;
+
+	rss->hdr_type = SXE2_RSS_ANY_HEADERS;
+	if (!is_inner) {
+		rss->hdr_type = SXE2_RSS_OUTER_HEADERS;
+		goto l_end;
+	}
+	if (sxe2_test_bit(SXE2_FLOW_HDR_IPV4, hdr)) {
+		if (sxe2_test_bit(SXE2_FLOW_HDR_UDP, hdr)) {
+			if (sxe2_test_bit(SXE2_FLOW_HDR_GRE, hdr)) {
+				rss->hdr_type =
+					SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV4_UDP_GRE;
+			} else if (sxe2_test_bit(SXE2_FLOW_HDR_GENEVE, hdr)) {
+				rss->hdr_type =
+					SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV4_UDP_GENEVE;
+			} else if (sxe2_test_bit(SXE2_FLOW_HDR_VXLAN, hdr)) {
+				rss->hdr_type =
+					SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV4_UDP_VXLAN;
+			} else if (sxe2_test_bit(SXE2_FLOW_HDR_GTPU, hdr)) {
+				rss->hdr_type =
+					SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV4_UDP_GTPU;
+			}
+		} else {
+			if (sxe2_test_bit(SXE2_FLOW_HDR_GRE, hdr)) {
+				rss->hdr_type =
+					SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV4_GRE;
+			} else {
+				rss->hdr_type = SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV4;
+			}
+		}
+	} else if (sxe2_test_bit(SXE2_FLOW_HDR_IPV6, hdr)) {
+		if (sxe2_test_bit(SXE2_FLOW_HDR_UDP, hdr)) {
+			if (sxe2_test_bit(SXE2_FLOW_HDR_GRE, hdr)) {
+				rss->hdr_type =
+					SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV6_UDP_GRE;
+			} else if (sxe2_test_bit(SXE2_FLOW_HDR_GENEVE, hdr)) {
+				rss->hdr_type =
+					SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV6_UDP_GENEVE;
+			} else if (sxe2_test_bit(SXE2_FLOW_HDR_VXLAN, hdr)) {
+				rss->hdr_type =
+					SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV6_UDP_VXLAN;
+			} else if (sxe2_test_bit(SXE2_FLOW_HDR_GTPU, hdr)) {
+				rss->hdr_type =
+					SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV6_UDP_GTPU;
+			}
+		} else {
+			if (sxe2_test_bit(SXE2_FLOW_HDR_GRE, hdr)) {
+				rss->hdr_type =
+					SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV6_GRE;
+			} else {
+				rss->hdr_type = SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV6;
+			}
+		}
+	}
+
+l_end:
+	if (rss->hdr_type == SXE2_RSS_ANY_HEADERS) {
+		ret = -EINVAL;
+		PMD_LOG_ERR(DRV, "Unsupported rss hdr type.");
+	}
+	return ret;
+}
+
+static int32_t sxe2_flow_set_rss_action_level(uint32_t level,
+		struct sxe2_flow *flow, struct rte_flow_error *error)
+{
+	bool is_inner = false;
+	struct sxe2_flow_action_rss *rss = &flow->action.rss;
+	int32_t ret = 0;
+
+	if (flow->meta.tunnel_type != SXE2_FLOW_TUNNEL_TYPE_NONE) {
+		if (level == 0 || level == 2)
+			is_inner = true;
+		else if (level == 1)
+			is_inner = false;
+	} else {
+		if (level == 2) {
+			ret = -EINVAL;
+			rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+				"RSS hash level 2 is not allowed no tunnel flow.");
+			PMD_LOG_ERR(DRV, "RSS hash level 2 is not allowed no tunnel flow.");
+			goto l_end;
+		}
+		is_inner = false;
+	}
+	rss->is_inner = is_inner;
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_set_rss_action_type(uint64_t rss_type,
+		struct sxe2_flow *flow, struct rte_flow_error *error)
+{
+	int32_t ret = 0;
+	struct sxe2_flow_pattern *pattern = NULL;
+	struct sxe2_flow_action_rss *rss = &flow->action.rss;
+	BITMAP_TYPE *hdr;
+	BITMAP_TYPE *fld;
+	bool is_inner = rss->is_inner;
+
+	ret = sxe2_flow_check_rss_action_type_valid(rss_type);
+	if (ret) {
+		rte_flow_error_set(error, ENOTSUP,
+			RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+			"RSS hash type has invalid combination.");
+		PMD_LOG_ERR(DRV, "RSS hash type has invalid combination.");
+		goto l_end;
+	}
+
+	pattern = is_inner ? &flow->pattern_inner : &flow->pattern_outer;
+	ret = sxe2_flow_check_rss_action_type_with_pattern(pattern,
+			rss_type);
+	if (ret) {
+		rte_flow_error_set(error, EINVAL,
+			RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+			"RSS hash type is not allowed by pattern.");
+		PMD_LOG_ERR(DRV, "RSS hash type is not allowed by pattern.");
+		goto l_end;
+	}
+
+	sxe2_bitmap_copy(rss->hdr_out, flow->pattern_outer.hdrs,
+			SXE2_FLOW_HDR_MAX);
+	sxe2_bitmap_copy(rss->hdr_in, flow->pattern_inner.hdrs,
+			SXE2_FLOW_HDR_MAX);
+	hdr = is_inner ? rss->hdr_in : rss->hdr_out;
+	fld = rss->fld;
+	sxe2_flow_set_rss_action_type_l234(hdr, fld, rss_type);
+
+	ret = sxe2_flow_set_rss_action_hdr_type(flow, is_inner);
+	if (ret) {
+		rte_flow_error_set(error, ENOTSUP,
+			RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+			"Unsupported rss hdr type.");
+		PMD_LOG_ERR(DRV, "Unsupported rss hdr type.");
+		goto l_end;
+	}
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_parse_action_rss(const struct rte_flow_action *action,
+				      struct rte_flow_error *error,
+				      struct sxe2_flow *flow)
+{
+	const struct rte_flow_action_rss *rss = action->conf;
+	int32_t ret = 0;
+	uint64_t rss_type = rss->types;
+	enum rte_eth_hash_function rss_func = rss->func;
+	uint32_t level = rss->level;
+
+	rss_type = rte_eth_rss_hf_refine(rss_type);
+
+	ret = sxe2_flow_check_rss_action_attr(rss, error);
+	if (ret)
+		goto l_end;
+
+	ret = sxe2_flow_set_rss_action_func(rss_func, rss_type, flow, error);
+	if (ret)
+		goto l_end;
+
+	ret = sxe2_flow_set_rss_action_level(level, flow, error);
+	if (ret)
+		goto l_end;
+
+	ret = sxe2_flow_set_rss_action_type(rss_type, flow, error);
+	if (ret)
+		goto l_end;
+	sxe2_set_bit(SXE2_FLOW_ACTION_RSS, flow->action.act_types);
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_parse_action_qregion(struct rte_eth_dev *dev,
+		const struct rte_flow_action *action,
+		struct rte_flow_error *error, struct sxe2_flow *flow)
+{
+	int32_t ret = 0;
+	uint8_t i = 0;
+	const struct rte_flow_action_rss *rss = action->conf;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	if (rss->types != 0 || rss->key_len != 0) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
+				action, "Queue region not support rss types or key.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+	if (rss->queue_num <= 1) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
+				action, "Queue region size can't be 0 or 1.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	for (i = 0; i < rss->queue_num - 1; i++) {
+		if (rss->queue[i + 1] != rss->queue[i] + 1) {
+			rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
+					action, "Queue index for queue region is not continuous.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+	}
+	if (rss->queue[rss->queue_num - 1] >= adapter->dev_info.dev_data->nb_rx_queues) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
+				action, "Queue index for queue region is out of range.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (!(rte_is_power_of_2(rss->queue_num))) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
+				action, "Queue region size must be power of 2.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+	flow->action.q_region.vsi_index = adapter->vsi_ctxt.dpdk_vsi_id;
+	flow->action.q_region.q_index = rss->queue[0];
+	flow->action.q_region.region = (uint8_t)rte_log2_u32(rss->queue_num);
+	sxe2_set_bit(SXE2_FLOW_ACTION_Q_REGION, flow->action.act_types);
+l_end:
+	return ret;
+}
+
+
+static int32_t sxe2_flow_parse_action_queue(struct rte_eth_dev *dev,
+					const struct rte_flow_action *action,
+					struct rte_flow_error *error,
+					struct sxe2_flow *flow)
+{
+	int32_t ret = 0;
+	const struct rte_flow_action_queue *queue = action->conf;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	if (queue->index >= adapter->dev_info.dev_data->nb_rx_queues) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
+			action, "Invalid queue index.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+	flow->action.queue.vsi_index = adapter->vsi_ctxt.dpdk_vsi_id;
+	flow->action.queue.q_index = queue->index;
+	sxe2_set_bit(SXE2_FLOW_ACTION_QUEUE, flow->action.act_types);
+l_end:
+	return ret;
+}
+
+
+static int32_t sxe2_flow_parse_action_represented_port(struct rte_eth_dev *dev,
+						   const struct rte_flow_action *action,
+						   struct rte_flow_error *error,
+						   struct sxe2_flow *flow)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	const struct rte_flow_action_ethdev *action_ethdev_conf;
+	const struct rte_eth_dev *dst_repr_dev;
+	uint16_t dst_repr_vsi_id;
+	uint16_t dst_backer_port_id;
+	uint16_t src_backer_port_id;
+	int32_t ret = 0;
+
+	if (!adapter->switchdev_info.is_switchdev) {
+		rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+				"Represented port action only support in switchdev mode.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (adapter->dev_type == SXE2_DEV_T_VF) {
+		rte_flow_error_set(error, ENOTSUP,
+			RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+			"Failed to cfg vf dev type.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	action_ethdev_conf = action->conf;
+	if (!rte_eth_dev_is_valid_port(action_ethdev_conf->port_id)) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
+				action, "Invalid port for represented port action.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	dst_repr_dev = &rte_eth_devices[action_ethdev_conf->port_id];
+	if (!dst_repr_dev->data) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
+				action, "Invalid port for represented port action.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	dst_backer_port_id = dst_repr_dev->data->backer_port_id;
+	if (adapter->is_dev_repr)
+		src_backer_port_id = dev->data->backer_port_id;
+	else
+		src_backer_port_id = adapter->dev_port_id;
+
+	if (src_backer_port_id != dst_backer_port_id) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
+				action, "Represented port action only support to cfg port in same device.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	ret = sxe2_flow_vsi_get_ethdev(dev, action_ethdev_conf->port_id, &dst_repr_vsi_id);
+	if (ret != 0) {
+		rte_flow_error_set(error, EINVAL,
+			RTE_FLOW_ERROR_TYPE_ACTION,
+			action, "Port representor action port dev invalid.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	sxe2_flow_action_vsi_merge(flow, dst_repr_vsi_id);
+l_end:
+	return ret;
+}
+
+
+static int32_t sxe2_flow_parse_action_port_representor(struct rte_eth_dev *dev,
+						   const struct rte_flow_action *action,
+						   struct rte_flow_error *error,
+						   struct sxe2_flow *flow)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	const struct rte_flow_action_ethdev *action_ethdev_conf;
+	uint16_t dst_vsi_id;
+	int32_t ret = 0;
+
+	if (!adapter->switchdev_info.is_switchdev) {
+		rte_flow_error_set(error, ENOTSUP,
+			RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+			"Port representor action only support in switchdev mode.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (adapter->dev_type == SXE2_DEV_T_VF) {
+		rte_flow_error_set(error, ENOTSUP,
+			RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+			"Cfg rule dev type is vf.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (!adapter->is_dev_repr) {
+		rte_flow_error_set(error, ENOTSUP,
+			RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+			"Cfg rule dev type is not repr.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	action_ethdev_conf = action->conf;
+	if (!action_ethdev_conf || !rte_eth_dev_is_valid_port(action_ethdev_conf->port_id)) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
+				action, "Invalid port for port representor action.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (dev->data->backer_port_id != action_ethdev_conf->port_id) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
+				action, "Invalid port for port representor.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	ret = sxe2_flow_vsi_get_ethdev(dev, action_ethdev_conf->port_id, &dst_vsi_id);
+	if (ret != 0) {
+		rte_flow_error_set(error, EINVAL,
+			RTE_FLOW_ERROR_TYPE_ACTION,
+			action, "Port representor action port dev invalid.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	sxe2_flow_action_vsi_merge(flow, dst_vsi_id);
+l_end:
+	return ret;
+}
+
+int32_t sxe2_flow_parse_action_port_id(struct rte_eth_dev *dev,
+				   const struct rte_flow_action *action,
+				   struct rte_flow_error *error,
+				   struct sxe2_flow *flow)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	const struct rte_flow_action_port_id *action_port_id_conf;
+	uint16_t dst_port_id;
+	uint16_t dst_vsi_id;
+	int32_t ret = 0;
+
+	action_port_id_conf = (const struct rte_flow_action_port_id *)action->conf;
+	dst_port_id = action_port_id_conf->original ?
+			adapter->dev_port_id : action_port_id_conf->id;
+
+	if (!rte_eth_dev_is_valid_port(dst_port_id)) {
+		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
+				action, "Invalid port id.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	ret = sxe2_flow_vsi_get_ethdev(dev, dst_port_id, &dst_vsi_id);
+	if (ret != 0) {
+		rte_flow_error_set(error, ENOTSUP,
+			RTE_FLOW_ERROR_TYPE_ACTION, action,
+			"Failed to cfg port dev invalid.");
+		goto l_end;
+	}
+
+	sxe2_flow_action_vsi_merge(flow, dst_vsi_id);
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_parse_action_send_to_kernel(struct rte_eth_dev *dev,
+			const struct rte_flow_action *action, struct rte_flow_error *error,
+			struct sxe2_flow *flow)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+
+	if (adapter->vsi_ctxt.kernel_vsi_id == UINT16_MAX) {
+		rte_flow_error_set(error, ENOTSUP,
+			RTE_FLOW_ERROR_TYPE_ACTION, action,
+			"Failed to cfg send to kernel action without kernel vsi.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	sxe2_flow_action_vsi_merge(flow, adapter->vsi_ctxt.kernel_vsi_id);
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flow_check_actions(struct rte_eth_dev *dev __rte_unused, struct sxe2_flow *flow,
+				   uint8_t action_num[], struct rte_flow_error *error)
+{
+	enum sxe2_flow_engine_type engine_type = flow->engine_type;
+	int32_t ret = 0;
+	int32_t dest_num = action_num[SXE2_FLOW_ACTION_Q_REGION] +
+			   action_num[SXE2_FLOW_ACTION_QUEUE];
+	int32_t vsi_num = action_num[SXE2_FLOW_ACTION_TO_VSI];
+	int32_t vsi_list_num = action_num[SXE2_FLOW_ACTION_TO_VSI_LIST];
+	int32_t pass_num = action_num[SXE2_FLOW_ACTION_PASSTHRU];
+	int32_t drop_num = action_num[SXE2_FLOW_ACTION_DROP];
+	int32_t mark_num = action_num[SXE2_FLOW_ACTION_MARK];
+	int32_t count_num = action_num[SXE2_FLOW_ACTION_COUNT];
+	int32_t rss_num = action_num[SXE2_FLOW_ACTION_RSS];
+	int32_t fwd_num = dest_num + vsi_num + vsi_list_num;
+	int32_t total_num = dest_num + vsi_num + vsi_list_num + pass_num +
+			drop_num + mark_num + count_num + rss_num;
+
+	if (pass_num > 1 || drop_num > 1 || mark_num > 1 ||
+			count_num > 1 || rss_num > 1 || dest_num > 1 ||
+			vsi_num > 1 || vsi_list_num > 1) {
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+				"ecah action can only be used once.");
+		PMD_LOG_ERR(DRV, "ecah action can only be used once.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (vsi_list_num && engine_type != SXE2_FLOW_ENGINE_SWITCH) {
+		rte_flow_error_set(error, EINVAL,
+			RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+			"VSI_LIST action is only supported for switch engine.");
+		PMD_LOG_ERR(DRV, "VSI_LIST action is only supported for switch engine.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (drop_num) {
+		if (total_num > drop_num + count_num) {
+			rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+				"Drop action can't be used with other actions unless count.");
+			PMD_LOG_ERR(DRV,
+				"Drop action can't be used with other actions unless count.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+	}
+
+	if (fwd_num > 1) {
+		rte_flow_error_set(error, EINVAL,
+			RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+			"Only supports one type of forwarding action.");
+		PMD_LOG_ERR(DRV, "Only supports one type of forwarding action.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (vsi_list_num) {
+		if (total_num > vsi_list_num) {
+			rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+				"VSI_LIST action can't be used with other actions.");
+			PMD_LOG_ERR(DRV,
+				"VSI_LIST action can't be used with other actions.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+	}
+
+	if (engine_type == SXE2_FLOW_ENGINE_FNAV) {
+		if (vsi_num) {
+			flow->action.q_region.q_index = 0;
+			flow->action.q_region.region = 7;
+			flow->action.q_region.vsi_index = flow->action.vsi.vsi_index;
+			sxe2_set_bit(SXE2_FLOW_ACTION_Q_REGION, flow->action.act_types);
+			dest_num++;
+		}
+	}
+
+l_end:
+	return ret;
+}
+
+
+int32_t sxe2_flow_parse_action(struct rte_eth_dev *dev,
+			const struct rte_flow_action actions[],
+			struct rte_flow_error *error,
+			struct sxe2_flow *flow)
+{
+	int32_t ret = 0;
+	const struct rte_flow_action *action;
+	const struct rte_flow_action_count *act_count;
+	const struct rte_flow_action_mark *act_mark;
+	uint8_t action_num[SXE2_FLOW_ACTION_MAX] = {0};
+	enum sxe2_flow_engine_type engine_type = flow->engine_type;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	sxe2_flow_action_pre(flow);
+
+	for (action = actions; action->type != RTE_FLOW_ACTION_TYPE_END; action++) {
+		switch (action->type) {
+		case RTE_FLOW_ACTION_TYPE_VOID:
+			break;
+		case RTE_FLOW_ACTION_TYPE_PASSTHRU:
+			if (engine_type == SXE2_FLOW_ENGINE_FNAV) {
+				sxe2_set_bit(SXE2_FLOW_ACTION_PASSTHRU, flow->action.act_types);
+				action_num[SXE2_FLOW_ACTION_PASSTHRU]++;
+			} else {
+				rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_ACTION, action,
+					"Passthru action is not supported for this flow.");
+				PMD_LOG_ERR(DRV,
+					"Passthru action is not supported for this flow.");
+				ret = -EINVAL;
+				goto l_end;
+			}
+			break;
+		case RTE_FLOW_ACTION_TYPE_DROP:
+			if (engine_type == SXE2_FLOW_ENGINE_ACL ||
+				engine_type == SXE2_FLOW_ENGINE_SWITCH ||
+				engine_type == SXE2_FLOW_ENGINE_FNAV) {
+				sxe2_set_bit(SXE2_FLOW_ACTION_DROP, flow->action.act_types);
+				action_num[SXE2_FLOW_ACTION_DROP]++;
+			} else {
+				rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_ACTION, action,
+					"Drop action is not supported for this flow.");
+				PMD_LOG_ERR(DRV,
+					"Drop action is not supported for this flow.");
+				ret = -EINVAL;
+				goto l_end;
+			}
+			break;
+		case RTE_FLOW_ACTION_TYPE_MARK:
+			if (engine_type == SXE2_FLOW_ENGINE_FNAV) {
+				sxe2_set_bit(SXE2_FLOW_ACTION_MARK, flow->action.act_types);
+				act_mark = action->conf;
+				flow->action.mark.mark_id = act_mark->id;
+				action_num[SXE2_FLOW_ACTION_MARK]++;
+			} else {
+				rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_ACTION, action,
+					"Mark action is not supported for this flow.");
+				PMD_LOG_ERR(DRV,
+					"Mark action is not supported for this flow.");
+				ret = -EINVAL;
+				goto l_end;
+			}
+			break;
+		case RTE_FLOW_ACTION_TYPE_COUNT:
+			if (engine_type == SXE2_FLOW_ENGINE_FNAV) {
+				sxe2_set_bit(SXE2_FLOW_ACTION_COUNT, flow->action.act_types);
+				act_count = action->conf;
+				flow->action.count.user_id = act_count->id;
+				flow->action.count.driver_id = 0;
+				if (flow->action.count.user_id == 0)
+					flow->action.count.driver_id =
+						++adapter->flow_ctxt.hw_res.global_index;
+				action_num[SXE2_FLOW_ACTION_COUNT]++;
+			} else {
+				rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_ACTION, action,
+					"Count action is not supported for this flow.");
+				PMD_LOG_ERR(DRV,
+					"Count action is not supported for this flow.");
+				ret = -EINVAL;
+				goto l_end;
+			}
+			break;
+		case RTE_FLOW_ACTION_TYPE_RSS:
+			if (engine_type == SXE2_FLOW_ENGINE_RSS) {
+				ret = sxe2_flow_parse_action_rss(action, error, flow);
+				if (ret != 0)
+					goto l_end;
+				action_num[SXE2_FLOW_ACTION_RSS]++;
+			} else if (engine_type == SXE2_FLOW_ENGINE_ACL ||
+				engine_type == SXE2_FLOW_ENGINE_SWITCH ||
+				engine_type == SXE2_FLOW_ENGINE_FNAV) {
+				ret = sxe2_flow_parse_action_qregion(dev, action, error, flow);
+				if (ret != 0)
+					goto l_end;
+				action_num[SXE2_FLOW_ACTION_Q_REGION]++;
+			} else {
+				rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_ACTION, action,
+					"RSS action is only supported for RSS flow.");
+				PMD_LOG_ERR(DRV,
+					"RSS action is only supported for RSS flow.");
+				ret = -EINVAL;
+				goto l_end;
+			}
+			break;
+		case RTE_FLOW_ACTION_TYPE_QUEUE:
+			if (engine_type == SXE2_FLOW_ENGINE_ACL ||
+				engine_type == SXE2_FLOW_ENGINE_SWITCH ||
+				engine_type == SXE2_FLOW_ENGINE_FNAV) {
+				ret = sxe2_flow_parse_action_queue(dev, action, error, flow);
+				if (ret != 0)
+					goto l_end;
+				action_num[SXE2_FLOW_ACTION_QUEUE]++;
+			} else {
+				rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_ACTION, action,
+					"Queue action is not supported for this flow.");
+				PMD_LOG_ERR(DRV,
+					"Queue action is not supported for this flow.");
+				ret = -EINVAL;
+				goto l_end;
+			}
+			break;
+		case RTE_FLOW_ACTION_TYPE_REPRESENTED_PORT:
+			if (engine_type == SXE2_FLOW_ENGINE_SWITCH) {
+				ret = sxe2_flow_parse_action_represented_port(dev,
+					action, error, flow);
+				if (ret != 0)
+					goto l_end;
+			} else {
+				rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_ACTION, action,
+					"REPRESENTED PORT action is only supported for SWITCH flow.");
+				PMD_LOG_ERR(DRV,
+					"REPRESENTED PORT action is only supported for SWITCH flow.");
+				ret = -EINVAL;
+				goto l_end;
+			}
+			break;
+		case RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR:
+			if (engine_type == SXE2_FLOW_ENGINE_SWITCH) {
+				ret = sxe2_flow_parse_action_port_representor(dev,
+					action, error, flow);
+				if (ret != 0)
+					goto l_end;
+			} else {
+				rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_ACTION, action,
+					"PORT REPRESENTOR action is only supported for SWITCH flow.");
+				PMD_LOG_ERR(DRV,
+					"PORT REPRESENTOR action is only supported for SWITCH flow.");
+				ret = -EINVAL;
+				goto l_end;
+			}
+			break;
+		case RTE_FLOW_ACTION_TYPE_PORT_ID:
+			if (engine_type == SXE2_FLOW_ENGINE_SWITCH ||
+				engine_type == SXE2_FLOW_ENGINE_ACL ||
+				engine_type == SXE2_FLOW_ENGINE_FNAV) {
+				ret = sxe2_flow_parse_action_port_id(dev, action,
+						error, flow);
+				if (ret != 0)
+					goto l_end;
+			} else {
+				rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_ACTION, action,
+					"PORT ID action is only supported for this flow.");
+				PMD_LOG_ERR(DRV,
+					"PORT ID action is only supported for this flow.");
+				ret = -EINVAL;
+				goto l_end;
+			}
+			break;
+		case RTE_FLOW_ACTION_TYPE_SEND_TO_KERNEL:
+			if (engine_type == SXE2_FLOW_ENGINE_ACL ||
+				engine_type == SXE2_FLOW_ENGINE_FNAV ||
+				engine_type == SXE2_FLOW_ENGINE_SWITCH) {
+				ret = sxe2_flow_parse_action_send_to_kernel(dev,
+						action, error, flow);
+				if (ret != 0)
+					goto l_end;
+			} else {
+				rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_ACTION, action,
+					"SEND TO KERNEL action is only supported for this flow.");
+				PMD_LOG_ERR(DRV,
+					"SEND TO KERNEL action is only supported for this flow.");
+				ret = -EINVAL;
+				goto l_end;
+			}
+			break;
+		default:
+			rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION, actions,
+				"Invalid action.");
+			PMD_LOG_ERR(DRV, "Invalid action type:%d", actions->type);
+			ret = -EINVAL;
+			goto l_end;
+		}
+	}
+
+	sxe2_flow_action_post(flow, action_num);
+
+	ret = sxe2_flow_check_actions(dev, flow, action_num, error);
+	if (ret != 0)
+		goto l_end;
+l_end:
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_flow_parse_action.h b/drivers/net/sxe2/sxe2_flow_parse_action.h
new file mode 100644
index 0000000000..479d10a522
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_flow_parse_action.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef SXE2_FLOW_PARSE_ACTION_H_
+#define SXE2_FLOW_PARSE_ACTION_H_
+#include <rte_flow_driver.h>
+
+#include "sxe2_osal.h"
+#include "sxe2_flow_define.h"
+
+
+int32_t sxe2_flow_parse_action(struct rte_eth_dev *dev,
+			const struct rte_flow_action actions[],
+			struct rte_flow_error *error,
+			struct sxe2_flow *flow);
+
+int32_t sxe2_flow_parse_action_port_id(struct rte_eth_dev *dev,
+			const struct rte_flow_action *action,
+			struct rte_flow_error *error,
+			struct sxe2_flow *flow);
+
+#endif /* SXE2_FLOW_PARSE_ACTION_H_ */
diff --git a/drivers/net/sxe2/sxe2_flow_parse_engine.c b/drivers/net/sxe2/sxe2_flow_parse_engine.c
new file mode 100644
index 0000000000..09de1b94c4
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_flow_parse_engine.c
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#include "sxe2_flow_parse_engine.h"
+#include "sxe2_ethdev.h"
+#include "sxe2_flow_public.h"
+#include "sxe2_flow_parse_action.h"
+#include "sxe2_common_log.h"
+
+static int32_t sxe2_flow_parse_engine_chk(struct sxe2_flow *flow,
+		struct rte_flow_error *error)
+{
+	int32_t ret = 0;
+
+	if (flow->engine_type == SXE2_FLOW_ENGINE_FNAV) {
+		if (flow->has_mask) {
+			ret = -EINVAL;
+			rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM_MASK, NULL,
+				"FNAV flow doesn't support mask");
+			PMD_LOG_ERR(DRV, "FNAV flow doesn't support mask");
+			goto l_end;
+		}
+		if (sxe2_test_bit(SXE2_FLOW_FLD_ID_S_VID,
+				flow->pattern_outer.map_spec) &&
+			sxe2_test_bit(SXE2_FLOW_FLD_ID_C_VID,
+				flow->pattern_outer.map_spec)) {
+			rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, NULL,
+				"Can't set double vid,please use tci.");
+			PMD_LOG_ERR(DRV,
+				"Can't set double vid,please use tci.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+	}
+l_end:
+	return ret;
+}
+
+int32_t sxe2_flow_parse_engine(struct rte_eth_dev *dev,
+		const struct rte_flow_attr *attr,
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error,
+		struct sxe2_flow *flow)
+{
+	int32_t ret = 0;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	const struct rte_flow_action *action;
+
+	if (flow->has_mask == 0 && flow->has_spec == 0) {
+		flow->engine_type = SXE2_FLOW_ENGINE_RSS;
+		goto l_end;
+	}
+
+	if (attr->group == 1) {
+		flow->engine_type = SXE2_FLOW_ENGINE_SWITCH;
+		goto l_end;
+	}
+	if (attr->group == 2) {
+		flow->engine_type = SXE2_FLOW_ENGINE_ACL;
+		goto l_end;
+	}
+	if (attr->group == 3) {
+		flow->engine_type = SXE2_FLOW_ENGINE_FNAV;
+		goto l_end;
+	}
+
+	if (adapter->is_dev_repr) {
+		flow->engine_type = SXE2_FLOW_ENGINE_SWITCH;
+		goto l_end;
+	}
+
+	if (adapter->switchdev_info.is_switchdev &&
+	    adapter->dev_type == SXE2_DEV_T_VF) {
+		flow->engine_type = SXE2_FLOW_ENGINE_FNAV;
+		goto l_end;
+	}
+
+	for (action = actions; action->type != RTE_FLOW_ACTION_TYPE_END; action++) {
+		switch (action->type) {
+		case RTE_FLOW_ACTION_TYPE_REPRESENTED_PORT:
+		case RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR:
+		case RTE_FLOW_ACTION_TYPE_PORT_ID:
+			flow->engine_type = SXE2_FLOW_ENGINE_SWITCH;
+			goto l_end;
+		default:
+			break;
+		}
+	}
+
+	if (adapter->switchdev_info.is_switchdev) {
+		flow->engine_type = SXE2_FLOW_ENGINE_FNAV;
+		goto l_end;
+	}
+
+	if (adapter->flow_isolated)
+		flow->engine_type = SXE2_FLOW_ENGINE_SWITCH;
+	else
+		flow->engine_type = SXE2_FLOW_ENGINE_FNAV;
+
+l_end:
+	ret = sxe2_flow_parse_engine_chk(flow, error);
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_flow_parse_engine.h b/drivers/net/sxe2/sxe2_flow_parse_engine.h
new file mode 100644
index 0000000000..1485beecb4
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_flow_parse_engine.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef SXE2_FLOW_PARSE_ENGINE_H_
+#define SXE2_FLOW_PARSE_ENGINE_H_
+#include "sxe2_osal.h"
+#include "sxe2_flow_define.h"
+
+int32_t sxe2_flow_parse_engine(struct rte_eth_dev *dev, const struct rte_flow_attr *attr,
+			const struct rte_flow_action actions[], struct rte_flow_error *error,
+			struct sxe2_flow *flow);
+#endif /* SXE2_FLOW_PARSE_ENGINE_H_ */
diff --git a/drivers/net/sxe2/sxe2_flow_parse_pattern.c b/drivers/net/sxe2/sxe2_flow_parse_pattern.c
new file mode 100644
index 0000000000..189abb1a33
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_flow_parse_pattern.c
@@ -0,0 +1,1822 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#include "sxe2_flow_parse_pattern.h"
+#include "rte_common.h"
+#include "rte_flow.h"
+#include "sxe2_common_log.h"
+#include "sxe2_ethdev.h"
+#include "sxe2_cmd_chnl.h"
+#include "sxe2_flow_define.h"
+
+const struct sxe2_flow_expand_node sxe2_support_expansion[SXE2_EXPANSION_MAX] = {
+	[SXE2_EXPANSION_OUTER_ETH] = {
+		.type = RTE_FLOW_ITEM_TYPE_ETH,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_NONE,
+		.is_tunnel = false,
+		.name = "eth",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_OUTER_VLAN,
+					      SXE2_EXPANSION_OUTER_IPV4,
+					      SXE2_EXPANSION_OUTER_IPV6,
+					      SXE2_EXPANSION_END),
+	},
+	[SXE2_EXPANSION_OUTER_VLAN] = {
+		.type = RTE_FLOW_ITEM_TYPE_VLAN,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_NONE,
+		.is_tunnel = false,
+		.name = "vlan",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_OUTER_QINQ,
+					      SXE2_EXPANSION_OUTER_IPV4,
+					      SXE2_EXPANSION_OUTER_IPV6,
+					      SXE2_EXPANSION_OUTER_END),
+	},
+	[SXE2_EXPANSION_OUTER_QINQ] = {
+		.type = RTE_FLOW_ITEM_TYPE_VLAN,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_NONE,
+		.is_tunnel = false,
+		.name = "vlan",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_OUTER_IPV4,
+					      SXE2_EXPANSION_OUTER_IPV6,
+					      SXE2_EXPANSION_OUTER_END),
+	},
+	[SXE2_EXPANSION_OUTER_IPV4] = {
+		.type = RTE_FLOW_ITEM_TYPE_IPV4,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_IPIP,
+		.is_tunnel = false,
+		.name = "ipv4",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_OUTER_UDP,
+					      SXE2_EXPANSION_OUTER_TCP,
+					      SXE2_EXPANSION_OUTER_SCTP,
+					      SXE2_EXPANSION_GRE,
+					      SXE2_EXPANSION_NVGRE,
+					      SXE2_EXPANSION_IPV4,
+					      SXE2_EXPANSION_IPV6,
+					      SXE2_EXPANSION_OUTER_END),
+	},
+	[SXE2_EXPANSION_OUTER_IPV6] = {
+		.type = RTE_FLOW_ITEM_TYPE_IPV6,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_IPIP,
+		.is_tunnel = false,
+		.name = "ipv6",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_OUTER_IPV6_FRAG_EXT,
+					      SXE2_EXPANSION_OUTER_UDP,
+					      SXE2_EXPANSION_OUTER_TCP,
+					      SXE2_EXPANSION_OUTER_SCTP,
+					      SXE2_EXPANSION_GRE,
+					      SXE2_EXPANSION_NVGRE,
+					      SXE2_EXPANSION_ETH,
+					      SXE2_EXPANSION_IPV4,
+					      SXE2_EXPANSION_IPV6,
+					      SXE2_EXPANSION_OUTER_END),
+	},
+	[SXE2_EXPANSION_OUTER_IPV6_FRAG_EXT] = {
+		.type = RTE_FLOW_ITEM_TYPE_IPV6_FRAG_EXT,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_NONE,
+		.is_tunnel = false,
+		.name = "ipv6_frag_ext",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_OUTER_END),
+	},
+	[SXE2_EXPANSION_OUTER_UDP] = {
+		.type = RTE_FLOW_ITEM_TYPE_UDP,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_NONE,
+		.is_tunnel = false,
+		.name = "udp",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_VXLAN,
+					      SXE2_EXPANSION_VXLAN_GPE,
+					      SXE2_EXPANSION_GENEVE,
+					      SXE2_EXPANSION_GTPU,
+					      SXE2_EXPANSION_GRE,
+					      SXE2_EXPANSION_NVGRE,
+					      SXE2_EXPANSION_OUTER_END),
+	},
+	[SXE2_EXPANSION_OUTER_TCP] = {
+		.type = RTE_FLOW_ITEM_TYPE_TCP,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_NONE,
+		.is_tunnel = false,
+		.name = "tcp",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_OUTER_END),
+	},
+	[SXE2_EXPANSION_OUTER_SCTP] = {
+		.type = RTE_FLOW_ITEM_TYPE_SCTP,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_NONE,
+		.is_tunnel = false,
+		.name = "sctp",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_OUTER_END),
+	},
+	[SXE2_EXPANSION_OUTER_END] = {
+		.type = RTE_FLOW_ITEM_TYPE_END,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_NONE,
+		.is_tunnel = false,
+		.name = "end",
+		.next = SXE2_FLOW_EXPAND_NEXT(0),
+	},
+	[SXE2_EXPANSION_VXLAN] = {
+		.type = RTE_FLOW_ITEM_TYPE_VXLAN,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_VXLAN,
+		.is_tunnel = true,
+		.name = "vxlan",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_ETH,
+					      SXE2_EXPANSION_IPV4,
+					      SXE2_EXPANSION_IPV6,
+					      SXE2_EXPANSION_END),
+	},
+	[SXE2_EXPANSION_VXLAN_GPE] = {
+		.type = RTE_FLOW_ITEM_TYPE_VXLAN_GPE,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_VXLAN,
+		.is_tunnel = true,
+		.name = "vxlan_gpe",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_ETH,
+					      SXE2_EXPANSION_IPV4,
+					      SXE2_EXPANSION_IPV6,
+					      SXE2_EXPANSION_END),
+	},
+	[SXE2_EXPANSION_GRE] = {
+		.type = RTE_FLOW_ITEM_TYPE_GRE,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_GRE,
+		.is_tunnel = true,
+		.name = "gre",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_ETH,
+					      SXE2_EXPANSION_IPV4,
+					      SXE2_EXPANSION_IPV6,
+					      SXE2_EXPANSION_END),
+	},
+	[SXE2_EXPANSION_NVGRE] = {
+		.type = RTE_FLOW_ITEM_TYPE_NVGRE,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_GRE,
+		.is_tunnel = true,
+		.name = "nvgre",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_ETH,
+					      SXE2_EXPANSION_IPV4,
+					      SXE2_EXPANSION_IPV6,
+					      SXE2_EXPANSION_END),
+	},
+	[SXE2_EXPANSION_GENEVE] = {
+		.type = RTE_FLOW_ITEM_TYPE_GENEVE,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_GENEVE,
+		.is_tunnel = true,
+		.name = "geneve",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_ETH,
+					      SXE2_EXPANSION_IPV4,
+					      SXE2_EXPANSION_IPV6,
+					      SXE2_EXPANSION_END),
+	},
+	[SXE2_EXPANSION_GTPU] = {
+		.type = RTE_FLOW_ITEM_TYPE_GTPU,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_GTPU,
+		.is_tunnel = true,
+		.name = "gtpu",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_IPV4,
+					      SXE2_EXPANSION_IPV6,
+					      SXE2_EXPANSION_END),
+	},
+	[SXE2_EXPANSION_ETH] = {
+		.type = RTE_FLOW_ITEM_TYPE_ETH,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_PARENT,
+		.is_tunnel = true,
+		.name = "eth",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_VLAN,
+					      SXE2_EXPANSION_IPV4,
+					      SXE2_EXPANSION_IPV6,
+					      SXE2_EXPANSION_END),
+	},
+	[SXE2_EXPANSION_VLAN] = {
+		.type = RTE_FLOW_ITEM_TYPE_VLAN,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_PARENT,
+		.is_tunnel = true,
+		.name = "vlan",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_IPV4,
+					      SXE2_EXPANSION_IPV6,
+					      SXE2_EXPANSION_END),
+	},
+	[SXE2_EXPANSION_IPV4] = {
+		.type = RTE_FLOW_ITEM_TYPE_IPV4,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_PARENT,
+		.is_tunnel = true,
+		.name = "ipv4",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_UDP,
+					      SXE2_EXPANSION_TCP,
+					      SXE2_EXPANSION_SCTP,
+					      SXE2_EXPANSION_END),
+	},
+	[SXE2_EXPANSION_IPV6] = {
+		.type = RTE_FLOW_ITEM_TYPE_IPV6,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_PARENT,
+		.is_tunnel = true,
+		.name = "ipv6",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_UDP,
+					      SXE2_EXPANSION_TCP,
+					      SXE2_EXPANSION_SCTP,
+					      SXE2_EXPANSION_IPV6_FRAG_EXT,
+					      SXE2_EXPANSION_END),
+	},
+	[SXE2_EXPANSION_UDP] = {
+		.type = RTE_FLOW_ITEM_TYPE_UDP,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_PARENT,
+		.is_tunnel = true,
+		.name = "udp",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_END),
+	},
+	[SXE2_EXPANSION_TCP] = {
+		.type = RTE_FLOW_ITEM_TYPE_TCP,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_PARENT,
+		.is_tunnel = true,
+		.name = "tcp",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_END),
+	},
+	[SXE2_EXPANSION_SCTP] = {
+		.type = RTE_FLOW_ITEM_TYPE_SCTP,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_PARENT,
+		.is_tunnel = true,
+		.name = "sctp",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_END),
+	},
+	[SXE2_EXPANSION_IPV6_FRAG_EXT] = {
+		.type = RTE_FLOW_ITEM_TYPE_IPV6_FRAG_EXT,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_PARENT,
+		.is_tunnel = true,
+		.name = "ipv6_frag_ext",
+		.next = SXE2_FLOW_EXPAND_NEXT(SXE2_EXPANSION_END),
+	},
+	[SXE2_EXPANSION_END] = {
+		.type = RTE_FLOW_ITEM_TYPE_END,
+		.tunnel_type = SXE2_FLOW_TUNNEL_TYPE_PARENT,
+		.is_tunnel = true,
+		.name = "end",
+		.next = SXE2_FLOW_EXPAND_NEXT(0),
+	}
+};
+
+const char *sxe2_flow_type_name[SXE2_FLOW_TYPE_MAX] = {
+	[SXE2_FLOW_MAC_PAY] = "SXE2_FLOW_MAC_PAY",
+	[SXE2_FLOW_MAC_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_PAY] = "SXE2_FLOW_MAC_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_IPV4_PAY] = "SXE2_FLOW_MAC_IPV4_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV4_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_IPV6_PAY] = "SXE2_FLOW_MAC_IPV4_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV4_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_IPV4_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_IPV6_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_MAC_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV4_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV6_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV4_FRAG_PAY] =
+	"SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV4_PAY] = "SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV4_UDP_PAY] =
+	"SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV4_TCP_PAY] =
+	"SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV4_SCTP_PAY] =
+	"SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV6_FRAG_PAY] =
+	"SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV6_PAY] =
+	"SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV6_UDP_PAY] =
+	"SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV6_TCP_PAY] =
+	"SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV6_SCTP_PAY] =
+	"SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_PAY] = "SXE2_FLOW_MAC_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_IPV4_PAY] = "SXE2_FLOW_MAC_IPV6_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV6_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_IPV6_PAY] = "SXE2_FLOW_MAC_IPV6_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV6_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_IPV4_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_IPV6_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV4_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV6_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV4_FRAG_PAY] =
+	"SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV4_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV6_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GTPU_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GTPU_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GTPU_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GTPU_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV4_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV4_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV6_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV6_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_IPV4_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_IPV6_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_VLAN_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_VLAN_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV4_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV6_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV4_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV6_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV4_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV6_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV4_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV6_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV4_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV6_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV4_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV6_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV4_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV6_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV4_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV6_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV4_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV6_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV4_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV6_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV4_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV6_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV4_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV6_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV6_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV4_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV4_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV4_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV4_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV4_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV4_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV4_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV4_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV4_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV4_SCTP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV6_FRAG_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV6_FRAG_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV6_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV6_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV6_UDP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV6_UDP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV6_TCP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV6_TCP_PAY",
+	[SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV6_SCTP_PAY] = "SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV6_SCTP_PAY",
+};
+#define SXE2_FLOW_TYPE_NAME_MAX_LEN 128
+
+static int32_t sxe2_flow_get_flow_type(struct sxe2_flow *flow)
+{
+	int32_t ret = -EINVAL;
+	uint16_t i = 0;
+	uint16_t len = 0;
+	char flow_type_name[SXE2_FLOW_TYPE_NAME_MAX_LEN] = {0};
+	len = snprintf(flow_type_name, sizeof(flow_type_name), "SXE2_FLOW_");
+	i += len;
+	if (sxe2_test_bit(SXE2_FLOW_HDR_ETH, flow->pattern_outer.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"MAC_");
+		i += len;
+	}
+	if (sxe2_test_bit(SXE2_FLOW_HDR_IPV4, flow->pattern_outer.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"IPV4_");
+		i += len;
+	} else if (sxe2_test_bit(SXE2_FLOW_HDR_IPV6, flow->pattern_outer.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"IPV6_");
+		i += len;
+	}
+	if (sxe2_test_bit(SXE2_FLOW_HDR_IPV_FRAG, flow->pattern_outer.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"FRAG_");
+		i += len;
+	}
+	if (sxe2_test_bit(SXE2_FLOW_HDR_UDP, flow->pattern_outer.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"UDP_");
+		i += len;
+	} else if (sxe2_test_bit(SXE2_FLOW_HDR_TCP, flow->pattern_outer.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"TCP_");
+		i += len;
+	} else if (sxe2_test_bit(SXE2_FLOW_HDR_SCTP, flow->pattern_outer.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"SCTP_");
+		i += len;
+	}
+
+	if (sxe2_test_bit(SXE2_FLOW_HDR_VXLAN, flow->pattern_outer.hdrs) ||
+	    sxe2_test_bit(SXE2_FLOW_HDR_GENEVE, flow->pattern_outer.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"VXGEN_");
+		i += len;
+	}
+	if (sxe2_test_bit(SXE2_FLOW_HDR_GRE, flow->pattern_outer.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"GRE_");
+		i += len;
+	}
+	if (sxe2_test_bit(SXE2_FLOW_HDR_GTPU, flow->pattern_outer.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"GTPU_");
+		i += len;
+	}
+
+	if (sxe2_test_bit(SXE2_FLOW_HDR_ETH, flow->pattern_inner.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"MAC_");
+		i += len;
+	}
+	if (sxe2_test_bit(SXE2_FLOW_HDR_VLAN, flow->pattern_inner.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"VLAN_");
+		i += len;
+	}
+	if (sxe2_test_bit(SXE2_FLOW_HDR_IPV4, flow->pattern_inner.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"IPV4_");
+		i += len;
+	} else if (sxe2_test_bit(SXE2_FLOW_HDR_IPV6, flow->pattern_inner.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"IPV6_");
+		i += len;
+	}
+	if (sxe2_test_bit(SXE2_FLOW_HDR_IPV_FRAG, flow->pattern_inner.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"FRAG_");
+		i += len;
+	}
+	if (sxe2_test_bit(SXE2_FLOW_HDR_UDP, flow->pattern_inner.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"UDP_");
+		i += len;
+	} else if (sxe2_test_bit(SXE2_FLOW_HDR_TCP, flow->pattern_inner.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"TCP_");
+		i += len;
+	} else if (sxe2_test_bit(SXE2_FLOW_HDR_SCTP, flow->pattern_inner.hdrs)) {
+		len = snprintf(flow_type_name + i, sizeof(flow_type_name) - i,
+			"SCTP_");
+		i += len;
+	}
+
+	len = snprintf(flow_type_name + i, sizeof(flow_type_name), "PAY");
+	i += len;
+
+	for (i = 0; i < SXE2_FLOW_TYPE_MAX; i++) {
+		if (sxe2_flow_type_name[i] == NULL)
+			continue;
+		if (strcmp(flow_type_name, sxe2_flow_type_name[i]) == 0) {
+			flow->meta.flow_type = i;
+			ret = 0;
+			break;
+		}
+	}
+	if (ret != 0)
+		PMD_LOG_ERR(DRV,
+			"Unsupported flow type. %s is not supported.", flow_type_name);
+	return ret;
+}
+
+static int32_t sxe2_flow_is_expandable_item(const struct rte_flow_item *item)
+{
+	int32_t ret = -EINVAL;
+	switch (item->type) {
+	case RTE_FLOW_ITEM_TYPE_ETH:
+	case RTE_FLOW_ITEM_TYPE_VLAN:
+	case RTE_FLOW_ITEM_TYPE_IPV4:
+	case RTE_FLOW_ITEM_TYPE_IPV6:
+	case RTE_FLOW_ITEM_TYPE_IPV6_FRAG_EXT:
+	case RTE_FLOW_ITEM_TYPE_UDP:
+	case RTE_FLOW_ITEM_TYPE_TCP:
+	case RTE_FLOW_ITEM_TYPE_SCTP:
+	case RTE_FLOW_ITEM_TYPE_VXLAN:
+	case RTE_FLOW_ITEM_TYPE_VXLAN_GPE:
+	case RTE_FLOW_ITEM_TYPE_GRE:
+	case RTE_FLOW_ITEM_TYPE_NVGRE:
+	case RTE_FLOW_ITEM_TYPE_GENEVE:
+	case RTE_FLOW_ITEM_TYPE_GTPU:
+	case RTE_FLOW_ITEM_TYPE_VOID:
+	case RTE_FLOW_ITEM_TYPE_END:
+		ret = 0;
+		break;
+	default:
+		break;
+	}
+	return ret;
+}
+
+static int32_t sxe2_flow_valid_next_expansion(enum sxe2_expansion *current,
+	const struct rte_flow_item *item,
+	enum sxe2_flow_tunnel_type *tunnel_type, BITMAP_TYPE *flow_type)
+{
+	int32_t ret = -EINVAL;
+	const struct rte_flow_item *next;
+	const enum sxe2_expansion *next_expansion = current;
+	const struct sxe2_flow_expand_node *node;
+	uint8_t len = 0;
+	char typelist[512] = {0};
+	char error[1024] = {0};
+	enum sxe2_flow_tunnel_type tunnel_type_now = SXE2_FLOW_TUNNEL_TYPE_NONE;
+	enum sxe2_flow_tunnel_type tunnel_type_next = SXE2_FLOW_TUNNEL_TYPE_NONE;
+	uint8_t is_tunnel_now = 0;
+	uint8_t is_tunnel_next = 0;
+
+	if (item->type != sxe2_support_expansion[*current].type) {
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	next = item;
+	do {
+		next++;
+		if (next->type == RTE_FLOW_ITEM_TYPE_VOID)
+			continue;
+		break;
+	} while (1);
+
+	node = &sxe2_support_expansion[*current];
+	next_expansion = node->next;
+	while (*next_expansion != 0) {
+		len = strlen(typelist);
+		snprintf(typelist + len, sizeof(typelist) - len,
+			"%s|", sxe2_support_expansion[*next_expansion].name);
+		if (sxe2_support_expansion[*next_expansion].type == next->type) {
+			ret = 0;
+			break;
+		}
+		next_expansion++;
+	}
+	if (ret != 0) {
+		snprintf(error, sizeof(error),
+			"The next item of %s only can be one of [%s].",
+			sxe2_support_expansion[*current].name, typelist);
+		PMD_LOG_ERR(INIT, "Invalid pattern sequence. %s", error);
+		goto l_end;
+	}
+	tunnel_type_now = sxe2_support_expansion[*current].tunnel_type;
+	tunnel_type_next = sxe2_support_expansion[*next_expansion].tunnel_type;
+	is_tunnel_now = sxe2_support_expansion[*current].is_tunnel;
+	is_tunnel_next = sxe2_support_expansion[*next_expansion].is_tunnel;
+
+
+	if (!is_tunnel_now && is_tunnel_next) {
+		if (tunnel_type_next == SXE2_FLOW_TUNNEL_TYPE_PARENT)
+			*tunnel_type = tunnel_type_now;
+		else
+			*tunnel_type = tunnel_type_next;
+	}
+
+l_end:
+	sxe2_set_bit(*current, flow_type);
+	*current = *next_expansion;
+	return ret;
+}
+
+static int32_t sxe2_flow_parse_pattern_eth(const struct rte_flow_item *item,
+					struct rte_flow_error *error,
+					struct sxe2_flow *flow,
+					enum sxe2_expansion next,
+					bool is_inner)
+{
+	const struct rte_flow_item_eth *eth_spec;
+	const struct rte_flow_item_eth *eth_mask;
+	const struct rte_ether_addr *dst_addr_mask;
+	const struct rte_ether_addr *src_addr_mask;
+	const struct rte_ether_addr *dst_addr_spec;
+	const struct rte_ether_addr *src_addr_spec;
+	rte_be16_t type_mask;
+	rte_be16_t type_spec;
+	struct sxe2_flow_pattern *pattern = is_inner ? &flow->pattern_inner : &flow->pattern_outer;
+	int32_t ret = 0;
+	uint16_t ether_type;
+	eth_spec = item->spec;
+	eth_mask = item->mask;
+
+	if (eth_spec == NULL && eth_mask == NULL)
+		goto l_end;
+
+	dst_addr_mask = &eth_mask->hdr.dst_addr;
+	src_addr_mask = &eth_mask->hdr.src_addr;
+	dst_addr_spec = &eth_spec->hdr.dst_addr;
+	src_addr_spec = &eth_spec->hdr.src_addr;
+	type_mask = eth_mask->hdr.ether_type;
+	type_spec = eth_spec->hdr.ether_type;
+
+	if (eth_mask->has_vlan) {
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM,
+				item, "Unsupported eth mask has_vlan.");
+		PMD_LOG_ERR(DRV, "Unsupported eth mask has_vlan");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (!rte_is_zero_ether_addr(dst_addr_mask)) {
+		if (!rte_is_broadcast_ether_addr(dst_addr_mask))
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_ETH_DA, pattern->map_mask);
+
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_ETH_DA, pattern->map_spec);
+		rte_memcpy(pattern->item_spec.eth.dst_addr, dst_addr_spec,
+			RTE_ETHER_ADDR_LEN);
+		rte_memcpy(pattern->item_mask.eth.dst_addr, dst_addr_mask,
+			RTE_ETHER_ADDR_LEN);
+	}
+	if (!rte_is_zero_ether_addr(src_addr_mask)) {
+		if (!rte_is_broadcast_ether_addr(src_addr_mask))
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_ETH_SA, pattern->map_mask);
+
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_ETH_SA, pattern->map_spec);
+		rte_memcpy(pattern->item_spec.eth.src_addr, src_addr_spec,
+			RTE_ETHER_ADDR_LEN);
+		rte_memcpy(pattern->item_mask.eth.src_addr, src_addr_mask,
+			RTE_ETHER_ADDR_LEN);
+	}
+	if (type_mask != 0) {
+		if (type_mask != UINT16_MAX) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM,
+					item, "Unsupported eth ether_type mask");
+			PMD_LOG_ERR(DRV, "unsupported eth ether_type mask[0x%x].",
+				    type_mask);
+			ret = -EINVAL;
+			goto l_end;
+		}
+		if (next != SXE2_EXPANSION_OUTER_END && next != SXE2_EXPANSION_END) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM,
+					item, "Unsupported eth ether_type match with next item.");
+			PMD_LOG_ERR(DRV, "unsupported eth ether_type match with next item.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+		ether_type = rte_be_to_cpu_16(type_spec);
+		if (ether_type == RTE_ETHER_TYPE_IPV4 ||
+				ether_type == RTE_ETHER_TYPE_IPV6) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM,
+					item, "Ether_type unsupported ipv4/ipv6(0x0800/0x86DD).");
+			PMD_LOG_ERR(DRV, "Ether_type unsupported ipv4/ipv6(0x0800/0x86DD).");
+			ret = -EINVAL;
+			goto l_end;
+		}
+		if (ether_type == RTE_ETHER_TYPE_VLAN || ether_type == RTE_ETHER_TYPE_QINQ ||
+				ether_type == RTE_ETHER_TYPE_QINQ1) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM,
+					item, "Ether_type unsupported vlan(0x8100/0x88a8/0x9100).");
+			PMD_LOG_ERR(DRV, "Ether_type unsupported vlan(0x8100/0x88a8/0x9100).");
+			ret = -EINVAL;
+			goto l_end;
+		}
+
+		if (ether_type <= SXE2_FLOW_ETH_TYPE_MIN) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM,
+					item, "Ether_type need max 1500.");
+			PMD_LOG_ERR(DRV, "Ether_type need max 1500.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_ETH_TYPE, pattern->map_spec);
+		pattern->item_spec.eth.ether_type = type_spec;
+		pattern->item_mask.eth.ether_type = type_mask;
+	}
+l_end:
+	sxe2_set_bit(SXE2_FLOW_HDR_ETH, pattern->hdrs);
+	pattern->rss_type_allow |= RTE_ETH_RSS_ETH;
+	pattern->rss_type_allow |= RTE_ETH_RSS_L2_DST_ONLY;
+	pattern->rss_type_allow |= RTE_ETH_RSS_L2_SRC_ONLY;
+	if (next == SXE2_EXPANSION_OUTER_END || next == SXE2_EXPANSION_END)
+		pattern->rss_type_allow |= RTE_ETH_RSS_L2_PAYLOAD;
+	return ret;
+}
+
+static int32_t sxe2_flow_parse_pattern_vlan(const struct rte_flow_item *item,
+				struct rte_flow_error *error,
+				struct sxe2_flow *flow,
+				enum sxe2_expansion next __rte_unused,
+				bool is_inner)
+{
+	const struct rte_flow_item_vlan *vlan_spec;
+	const struct rte_flow_item_vlan *vlan_mask;
+	struct sxe2_flow_pattern *pattern = is_inner ? &flow->pattern_inner : &flow->pattern_outer;
+	rte_be16_t vlan_tci_mask;
+	rte_be16_t vlan_tci_spec;
+	rte_be16_t eth_proto_mask;
+	rte_be16_t eth_proto_spec;
+	int32_t ret = 0;
+	vlan_spec = item->spec;
+	vlan_mask = item->mask;
+	bool is_qinq = false;
+
+	if (sxe2_test_bit(SXE2_FLOW_HDR_VLAN, pattern->hdrs))
+		is_qinq = true;
+
+	if (vlan_spec == NULL && vlan_mask == NULL)
+		goto l_end;
+
+	vlan_tci_mask = vlan_mask->hdr.vlan_tci;
+	vlan_tci_spec = vlan_spec->hdr.vlan_tci;
+	eth_proto_mask = vlan_mask->hdr.eth_proto;
+	eth_proto_spec = vlan_spec->hdr.eth_proto;
+
+	if (vlan_mask->has_more_vlan || vlan_mask->reserved) {
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM,
+				item, "Unsupported vlan mask has_qinq.");
+		PMD_LOG_ERR(DRV, "Unsupported vlan mask has_qinq");
+		ret = -EINVAL;
+		goto l_end;
+	}
+	if (vlan_tci_mask) {
+		if (vlan_tci_spec == 0) {
+			rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
+				item, "vlan id can't be 0.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+	}
+
+	if (eth_proto_mask) {
+		if (eth_proto_mask != UINT16_MAX) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM,
+					item, "Unsupported vlan ether_type mask");
+			PMD_LOG_ERR(DRV, "unsupported vlan ether_type mask.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+		if (eth_proto_spec != RTE_BE16(0x8100) &&
+			eth_proto_spec != RTE_BE16(0x88a8) &&
+			eth_proto_spec != RTE_BE16(0x9100)) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM,
+					item, "Unsupported vlan ether_type, only support 0x8100, 0x88a8 and 0x9100.");
+			PMD_LOG_ERR(DRV, "Unsupported vlan ether_type, only support 0x8100, 0x88a8 and 0x9100.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+	}
+	if (!is_qinq) {
+		if (vlan_tci_mask) {
+			if (vlan_tci_mask == RTE_BE16(0x0fff)) {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_S_VID, pattern->map_spec);
+			} else if (vlan_tci_mask == UINT16_MAX) {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_S_TCI, pattern->map_spec);
+			} else {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_S_TCI, pattern->map_mask);
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_S_TCI, pattern->map_spec);
+			}
+		}
+		if (eth_proto_mask)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_S_TPID, pattern->map_spec);
+	} else {
+		if (vlan_tci_mask) {
+			if (vlan_tci_mask == RTE_BE16(0x0fff)) {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_C_VID, pattern->map_spec);
+			} else if (vlan_tci_mask == UINT16_MAX) {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_C_TCI, pattern->map_spec);
+			} else {
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_C_TCI, pattern->map_mask);
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_C_TCI, pattern->map_spec);
+			}
+		}
+		if (eth_proto_mask)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_C_TPID, pattern->map_spec);
+	}
+	if (is_qinq) {
+		pattern->item_spec.qinq.type =  eth_proto_spec;
+		pattern->item_mask.qinq.type =  eth_proto_mask;
+		pattern->item_spec.qinq.vlan = vlan_tci_spec;
+		pattern->item_mask.qinq.vlan = vlan_tci_mask;
+	} else {
+		pattern->item_spec.vlan.type = eth_proto_spec;
+		pattern->item_mask.vlan.type = eth_proto_mask;
+		pattern->item_spec.vlan.vlan = vlan_tci_spec;
+		pattern->item_mask.vlan.vlan = vlan_tci_mask;
+	}
+l_end:
+	pattern->rss_type_allow |= RTE_ETH_RSS_S_VLAN;
+	pattern->rss_type_allow |= RTE_ETH_RSS_C_VLAN;
+	if (!is_qinq)
+		sxe2_set_bit(SXE2_FLOW_HDR_VLAN, pattern->hdrs);
+	else
+		sxe2_set_bit(SXE2_FLOW_HDR_QINQ, pattern->hdrs);
+	return ret;
+}
+
+static int32_t sxe2_flow_parse_pattern_ipv4(const struct rte_flow_item *item,
+					struct rte_flow_error *error,
+					struct sxe2_flow *flow,
+					enum sxe2_expansion next,
+					bool is_inner)
+{
+	const struct rte_flow_item_ipv4 *ipv4_spec;
+	const struct rte_flow_item_ipv4 *ipv4_mask;
+	struct sxe2_flow_pattern *pattern = is_inner ? &flow->pattern_inner : &flow->pattern_outer;
+	int32_t ret = 0;
+	ipv4_spec = item->spec;
+	ipv4_mask = item->mask;
+
+	if (ipv4_mask == NULL && ipv4_spec == NULL)
+		goto l_end;
+
+	if (ipv4_mask->hdr.version_ihl || ipv4_mask->hdr.total_length ||
+			ipv4_mask->hdr.hdr_checksum || ipv4_mask->hdr.packet_id) {
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM,
+				item, "Unsupported some IPv4 mask.");
+		PMD_LOG_ERR(DRV, "Unsupported some IPv4 mask");
+		ret = -EINVAL;
+		goto l_end;
+	}
+	if (ipv4_mask->hdr.src_addr) {
+		if (ipv4_mask->hdr.src_addr != UINT32_MAX)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_SA, pattern->map_mask);
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_SA, pattern->map_spec);
+		pattern->item_spec.ipv4.saddr = ipv4_spec->hdr.src_addr;
+		pattern->item_mask.ipv4.saddr = ipv4_mask->hdr.src_addr;
+	}
+	if (ipv4_mask->hdr.dst_addr) {
+		if (ipv4_mask->hdr.dst_addr != UINT32_MAX)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_DA, pattern->map_mask);
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_DA, pattern->map_spec);
+		pattern->item_spec.ipv4.daddr = ipv4_spec->hdr.dst_addr;
+		pattern->item_mask.ipv4.daddr = ipv4_mask->hdr.dst_addr;
+	}
+
+	if (ipv4_mask->hdr.next_proto_id) {
+		if (next != SXE2_EXPANSION_OUTER_END && next != SXE2_EXPANSION_END) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM,
+					item, "IPv4 proto id must be the last partten.");
+			PMD_LOG_ERR(DRV, "IPv4 proto id must be the last partten.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+		if (ipv4_mask->hdr.next_proto_id != UINT8_MAX)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_PROT, pattern->map_mask);
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_PROT, pattern->map_spec);
+		pattern->item_spec.ipv4.protocol = ipv4_spec->hdr.next_proto_id;
+		pattern->item_mask.ipv4.protocol = ipv4_mask->hdr.next_proto_id;
+	}
+	if (ipv4_mask->hdr.time_to_live) {
+		if (ipv4_mask->hdr.time_to_live != UINT8_MAX)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_TTL, pattern->map_mask);
+		if (ipv4_spec->hdr.time_to_live == 0) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM,
+					item, "ipv4 ttl must be not 0.");
+			PMD_LOG_ERR(DRV, "ipv4 ttl must be not 0.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_TTL, pattern->map_spec);
+		pattern->item_spec.ipv4.ttl = ipv4_spec->hdr.time_to_live;
+		pattern->item_mask.ipv4.ttl = ipv4_mask->hdr.time_to_live;
+	}
+	if (ipv4_mask->hdr.type_of_service) {
+		if (ipv4_mask->hdr.type_of_service != UINT8_MAX)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_TOS, pattern->map_mask);
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_TOS, pattern->map_spec);
+		pattern->item_spec.ipv4.tos = ipv4_spec->hdr.type_of_service;
+		pattern->item_mask.ipv4.tos = ipv4_mask->hdr.type_of_service;
+	}
+	if (ipv4_mask->hdr.fragment_offset) {
+		if (ipv4_spec->hdr.fragment_offset == rte_cpu_to_be_16(RTE_IPV4_HDR_MF_FLAG) &&
+			ipv4_mask->hdr.fragment_offset == rte_cpu_to_be_16(RTE_IPV4_HDR_MF_FLAG)) {
+			if (next != SXE2_EXPANSION_OUTER_END && next != SXE2_EXPANSION_END) {
+				rte_flow_error_set(error, EINVAL,
+						RTE_FLOW_ERROR_TYPE_ITEM,
+						item, "IPv4 frag offset must be the last partten.");
+				PMD_LOG_ERR(DRV, "IPv4 frag offset must be the last partten.");
+				ret = -EINVAL;
+				goto l_end;
+			}
+			sxe2_set_bit(SXE2_FLOW_HDR_IPV_FRAG, pattern->hdrs);
+		} else {
+			rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM,
+				item, "Unsupported ipv4 fragment_offset cfg.");
+			PMD_LOG_ERR(DRV, "Unsupported ipv4 fragment_offset cfg.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+	}
+
+l_end:
+	sxe2_set_bit(SXE2_FLOW_HDR_IPV4, pattern->hdrs);
+	pattern->rss_type_allow |= RTE_ETH_RSS_IPV4;
+	pattern->rss_type_allow |= RTE_ETH_RSS_L3_SRC_ONLY;
+	pattern->rss_type_allow |= RTE_ETH_RSS_L3_DST_ONLY;
+	pattern->rss_type_allow |= RTE_ETH_RSS_IPV4_CHKSUM;
+	if (next == SXE2_EXPANSION_OUTER_END || next == SXE2_EXPANSION_END) {
+		pattern->rss_type_allow |= RTE_ETH_RSS_FRAG_IPV4;
+		pattern->rss_type_allow |= RTE_ETH_RSS_NONFRAG_IPV4_OTHER;
+	}
+
+	return ret;
+}
+
+static int32_t sxe2_flow_parse_pattern_ipv6(const struct rte_flow_item *item,
+				struct rte_flow_error *error,
+				struct sxe2_flow *flow,
+				enum sxe2_expansion next __rte_unused,
+				bool is_inner)
+{
+	const struct rte_flow_item_ipv6 *ipv6_spec;
+	const struct rte_flow_item_ipv6 *ipv6_mask;
+	uint32_t vtc_flow_mask;
+	uint32_t tc_mask;
+	struct sxe2_flow_pattern *pattern = is_inner ? &flow->pattern_inner : &flow->pattern_outer;
+	int32_t ret = 0;
+	ipv6_spec = item->spec;
+	ipv6_mask = item->mask;
+	uint8_t ipv6_addr_mask[16] = {
+		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+	};
+	uint8_t ipv6_addr_empty[16] = { 0 };
+
+	if (ipv6_mask == NULL && ipv6_spec == NULL)
+		goto l_end;
+
+	if (ipv6_mask->hdr.payload_len || ipv6_mask->has_hop_ext ||
+		ipv6_mask->has_route_ext || ipv6_mask->has_frag_ext ||
+		ipv6_mask->has_auth_ext || ipv6_mask->has_esp_ext ||
+		ipv6_mask->has_dest_ext || ipv6_mask->has_mobil_ext ||
+		ipv6_mask->has_hip_ext || ipv6_mask->has_shim6_ext) {
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM,
+				item, "Unsupported some IPv6 mask");
+		PMD_LOG_ERR(DRV, "Unsupported some IPv6 mask");
+		ret = -EINVAL;
+		goto l_end;
+	}
+	if (memcmp(&ipv6_mask->hdr.src_addr, ipv6_addr_empty,
+		   sizeof(ipv6_addr_empty)) != 0) {
+		if (memcmp(&ipv6_mask->hdr.src_addr, ipv6_addr_mask,
+			   sizeof(ipv6_addr_mask)) != 0)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_SA, pattern->map_mask);
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_SA, pattern->map_spec);
+		rte_memcpy(&pattern->item_spec.ipv6.saddr, &ipv6_spec->hdr.src_addr,
+			   sizeof(ipv6_spec->hdr.src_addr));
+		rte_memcpy(&pattern->item_mask.ipv6.saddr, &ipv6_mask->hdr.src_addr,
+			   sizeof(ipv6_mask->hdr.src_addr));
+	}
+	if (memcmp(&ipv6_mask->hdr.dst_addr, ipv6_addr_empty,
+		    sizeof(ipv6_addr_empty)) != 0) {
+		if (memcmp(&ipv6_mask->hdr.dst_addr, ipv6_addr_mask,
+			   sizeof(ipv6_addr_mask)) != 0)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_DA, pattern->map_mask);
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_DA, pattern->map_spec);
+		rte_memcpy(&pattern->item_spec.ipv6.daddr, &ipv6_spec->hdr.dst_addr,
+			   sizeof(ipv6_spec->hdr.dst_addr));
+		rte_memcpy(&pattern->item_mask.ipv6.daddr, &ipv6_mask->hdr.dst_addr,
+			   sizeof(ipv6_mask->hdr.dst_addr));
+	}
+	if (ipv6_mask->hdr.vtc_flow) {
+		vtc_flow_mask = rte_be_to_cpu_32(ipv6_mask->hdr.vtc_flow);
+		tc_mask = vtc_flow_mask & (SXE2_IPV6_TC_MASK << SXE2_IPV6_TC_SHIFT);
+		if (tc_mask != vtc_flow_mask) {
+			rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
+					item, "ipv6 vtc_flow only support TC mask.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+		if (tc_mask != (SXE2_IPV6_TC_MASK << SXE2_IPV6_TC_SHIFT))
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_DSCP, pattern->map_mask);
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_DSCP, pattern->map_spec);
+		pattern->item_spec.ipv6.pri_ver_flow = ipv6_spec->hdr.vtc_flow;
+		pattern->item_mask.ipv6.pri_ver_flow = ipv6_mask->hdr.vtc_flow;
+	}
+	if (ipv6_mask->hdr.proto) {
+		if (next != SXE2_EXPANSION_OUTER_END && next != SXE2_EXPANSION_END) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM,
+					item, "IPv6 proto id must be the last partten.");
+			PMD_LOG_ERR(DRV, "IPv6 proto id must be the last partten.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+		if (ipv6_mask->hdr.proto != UINT8_MAX)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_PROT, pattern->map_mask);
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_PROT, pattern->map_spec);
+		pattern->item_spec.ipv6.nexthdr = ipv6_spec->hdr.proto;
+		pattern->item_mask.ipv6.nexthdr = ipv6_mask->hdr.proto;
+	}
+	if (ipv6_mask->hdr.hop_limits) {
+		if (ipv6_mask->hdr.hop_limits != UINT8_MAX)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_TTL, pattern->map_mask);
+
+		if (ipv6_spec->hdr.hop_limits == 0) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM,
+					item, "ipv6 hop must be not 0.");
+			PMD_LOG_ERR(DRV, "ipv6 hop must be not 0.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_TTL, pattern->map_spec);
+		pattern->item_spec.ipv6.hop_limit = ipv6_spec->hdr.hop_limits;
+		pattern->item_mask.ipv6.hop_limit = ipv6_mask->hdr.hop_limits;
+	}
+
+l_end:
+	sxe2_set_bit(SXE2_FLOW_HDR_IPV6, pattern->hdrs);
+	pattern->rss_type_allow |= RTE_ETH_RSS_IPV6;
+	pattern->rss_type_allow |= RTE_ETH_RSS_L3_SRC_ONLY;
+	pattern->rss_type_allow |= RTE_ETH_RSS_L3_DST_ONLY;
+	pattern->rss_type_allow |= RTE_ETH_RSS_L3_PRE32;
+	pattern->rss_type_allow |= RTE_ETH_RSS_L3_PRE48;
+	pattern->rss_type_allow |= RTE_ETH_RSS_L3_PRE64;
+	if (next == SXE2_EXPANSION_OUTER_END || next == SXE2_EXPANSION_END)
+		pattern->rss_type_allow |= RTE_ETH_RSS_NONFRAG_IPV6_OTHER;
+	return ret;
+}
+
+static int32_t sxe2_flow_parse_pattern_ipv6_frag_ext(const struct rte_flow_item *item,
+					 struct rte_flow_error *error,
+					 struct sxe2_flow *flow,
+					 enum sxe2_expansion next __rte_unused,
+					 bool is_inner)
+{
+	const struct rte_flow_item_ipv6_frag_ext *ipv6_frag_spec;
+	const struct rte_flow_item_ipv6_frag_ext *ipv6_frag_mask;
+	struct sxe2_flow_pattern *pattern = is_inner ? &flow->pattern_inner : &flow->pattern_outer;
+	int32_t ret = 0;
+	ipv6_frag_spec = item->spec;
+	ipv6_frag_mask = item->mask;
+
+	if (ipv6_frag_mask == NULL && ipv6_frag_spec == NULL)
+		goto l_end;
+
+	if (ipv6_frag_mask->hdr.reserved || ipv6_frag_mask->hdr.frag_data ||
+	    ipv6_frag_mask->hdr.id || ipv6_frag_mask->hdr.next_header) {
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM,
+				item, "Unsupported some IPv6 frag ext mask");
+		PMD_LOG_ERR(DRV, "Unsupported some IPv6 frag ext mask");
+		ret = -EINVAL;
+		goto l_end;
+	}
+l_end:
+	sxe2_set_bit(SXE2_FLOW_HDR_IPV_FRAG, pattern->hdrs);
+	pattern->rss_type_allow |= RTE_ETH_RSS_FRAG_IPV6;
+	return ret;
+}
+
+static int32_t sxe2_flow_parse_pattern_tcp(const struct rte_flow_item *item,
+				struct rte_flow_error *error,
+				struct sxe2_flow *flow,
+				enum sxe2_expansion next __rte_unused,
+				bool is_inner)
+{
+	const struct rte_flow_item_tcp *tcp_spec;
+	const struct rte_flow_item_tcp *tcp_mask;
+	struct sxe2_flow_pattern *pattern = is_inner ? &flow->pattern_inner : &flow->pattern_outer;
+	int32_t ret = 0;
+	tcp_spec = item->spec;
+	tcp_mask = item->mask;
+
+	if (tcp_mask == NULL && tcp_spec == NULL)
+		goto l_end;
+
+	if (tcp_mask->hdr.sent_seq || tcp_mask->hdr.recv_ack ||
+	    tcp_mask->hdr.data_off || tcp_mask->hdr.tcp_flags ||
+	    tcp_mask->hdr.rx_win || tcp_mask->hdr.cksum ||
+	    tcp_mask->hdr.tcp_urp) {
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM,
+				item, "Unsupported some TCP mask");
+		PMD_LOG_ERR(DRV, "Unsupported some TCP mask");
+		ret = -EINVAL;
+		goto l_end;
+	}
+	if (tcp_mask->hdr.src_port) {
+		if (tcp_mask->hdr.src_port != UINT16_MAX)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_TCP_SRC_PORT, pattern->map_mask);
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_TCP_SRC_PORT, pattern->map_spec);
+		pattern->item_spec.tcp.source = tcp_spec->hdr.src_port;
+		pattern->item_mask.tcp.source = tcp_mask->hdr.src_port;
+	}
+	if (tcp_mask->hdr.dst_port) {
+		if (tcp_mask->hdr.dst_port != UINT16_MAX)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_TCP_DST_PORT, pattern->map_mask);
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_TCP_DST_PORT, pattern->map_spec);
+		pattern->item_spec.tcp.dest = tcp_spec->hdr.dst_port;
+		pattern->item_mask.tcp.dest = tcp_mask->hdr.dst_port;
+	}
+
+l_end:
+	sxe2_set_bit(SXE2_FLOW_HDR_TCP, pattern->hdrs);
+	if (sxe2_test_bit(SXE2_FLOW_HDR_IPV4, pattern->hdrs))
+		pattern->rss_type_allow |= RTE_ETH_RSS_NONFRAG_IPV4_TCP;
+	else if (sxe2_test_bit(SXE2_FLOW_HDR_IPV6, pattern->hdrs))
+		pattern->rss_type_allow |= RTE_ETH_RSS_NONFRAG_IPV6_TCP;
+
+	pattern->rss_type_allow |= RTE_ETH_RSS_L4_SRC_ONLY;
+	pattern->rss_type_allow |= RTE_ETH_RSS_L4_DST_ONLY;
+	pattern->rss_type_allow |= RTE_ETH_RSS_L4_CHKSUM;
+	return ret;
+}
+
+static int32_t sxe2_flow_parse_pattern_udp(const struct rte_flow_item *item,
+				struct rte_flow_error *error,
+				struct sxe2_flow *flow,
+				enum sxe2_expansion next __rte_unused,
+				bool is_inner)
+{
+	const struct rte_flow_item_udp *udp_spec;
+	const struct rte_flow_item_udp *udp_mask;
+	struct sxe2_flow_pattern *pattern = is_inner ? &flow->pattern_inner : &flow->pattern_outer;
+	int32_t ret = 0;
+	udp_spec = item->spec;
+	udp_mask = item->mask;
+
+	if (udp_mask == NULL && udp_spec == NULL)
+		goto l_end;
+
+	if (udp_mask->hdr.dgram_len || udp_mask->hdr.dgram_cksum) {
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM,
+				item, "Unsupported some UDP mask");
+		PMD_LOG_ERR(DRV, "Unsupported some UDP mask");
+		ret = -EINVAL;
+		goto l_end;
+	}
+	if (udp_mask->hdr.src_port) {
+		if (udp_mask->hdr.src_port != UINT16_MAX)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_UDP_SRC_PORT, pattern->map_mask);
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_UDP_SRC_PORT, pattern->map_spec);
+		pattern->item_spec.udp.source = udp_spec->hdr.src_port;
+		pattern->item_mask.udp.source = udp_mask->hdr.src_port;
+	}
+	if (udp_mask->hdr.dst_port) {
+		if (udp_mask->hdr.dst_port != UINT16_MAX)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_UDP_DST_PORT, pattern->map_mask);
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_UDP_DST_PORT, pattern->map_spec);
+		pattern->item_spec.udp.dest = udp_spec->hdr.dst_port;
+		pattern->item_mask.udp.dest = udp_mask->hdr.dst_port;
+	}
+
+l_end:
+	sxe2_set_bit(SXE2_FLOW_HDR_UDP, pattern->hdrs);
+	if (sxe2_test_bit(SXE2_FLOW_HDR_IPV4, pattern->hdrs))
+		pattern->rss_type_allow |= RTE_ETH_RSS_NONFRAG_IPV4_UDP;
+	else if (sxe2_test_bit(SXE2_FLOW_HDR_IPV6, pattern->hdrs))
+		pattern->rss_type_allow |= RTE_ETH_RSS_NONFRAG_IPV6_UDP;
+
+	pattern->rss_type_allow |= RTE_ETH_RSS_L4_SRC_ONLY;
+	pattern->rss_type_allow |= RTE_ETH_RSS_L4_DST_ONLY;
+	pattern->rss_type_allow |= RTE_ETH_RSS_L4_CHKSUM;
+	return ret;
+}
+
+static int32_t sxe2_flow_parse_pattern_sctp(const struct rte_flow_item *item,
+					struct rte_flow_error *error,
+					struct sxe2_flow *flow,
+					enum sxe2_expansion next __rte_unused,
+					bool is_inner)
+{
+	const struct rte_flow_item_sctp *sctp_spec;
+	const struct rte_flow_item_sctp *sctp_mask;
+	struct sxe2_flow_pattern *pattern = is_inner ? &flow->pattern_inner : &flow->pattern_outer;
+	int32_t ret = 0;
+	sctp_spec = item->spec;
+	sctp_mask = item->mask;
+
+	if (sctp_mask == NULL && sctp_spec == NULL)
+		goto l_end;
+
+	if (sctp_mask->hdr.cksum || sctp_mask->hdr.tag) {
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM,
+				item, "Unsupported some SCTP mask");
+		PMD_LOG_ERR(DRV, "Unsupported some SCTP mask");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (sctp_mask->hdr.src_port) {
+		if (sctp_mask->hdr.src_port != UINT16_MAX)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_SCTP_SRC_PORT, pattern->map_mask);
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_SCTP_SRC_PORT, pattern->map_spec);
+		pattern->item_spec.sctp.src_port = sctp_spec->hdr.src_port;
+		pattern->item_mask.sctp.src_port = sctp_mask->hdr.src_port;
+	}
+	if (sctp_mask->hdr.dst_port) {
+		if (sctp_mask->hdr.dst_port != UINT16_MAX)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_SCTP_DST_PORT, pattern->map_mask);
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_SCTP_DST_PORT, pattern->map_spec);
+		pattern->item_spec.sctp.dst_port = sctp_spec->hdr.dst_port;
+		pattern->item_mask.sctp.dst_port = sctp_mask->hdr.dst_port;
+	}
+
+l_end:
+	sxe2_set_bit(SXE2_FLOW_HDR_SCTP, pattern->hdrs);
+	if (sxe2_test_bit(SXE2_FLOW_HDR_IPV4, pattern->hdrs))
+		pattern->rss_type_allow |= RTE_ETH_RSS_NONFRAG_IPV4_SCTP;
+	else if (sxe2_test_bit(SXE2_FLOW_HDR_IPV6, pattern->hdrs))
+		pattern->rss_type_allow |= RTE_ETH_RSS_NONFRAG_IPV6_SCTP;
+	pattern->rss_type_allow |= RTE_ETH_RSS_L4_SRC_ONLY;
+	pattern->rss_type_allow |= RTE_ETH_RSS_L4_DST_ONLY;
+	pattern->rss_type_allow |= RTE_ETH_RSS_L4_CHKSUM;
+	return ret;
+}
+
+static int32_t sxe2_flow_parse_pattern_geneve(const struct rte_flow_item *item,
+							struct rte_flow_error *error,
+							struct sxe2_flow *flow,
+							enum sxe2_expansion next __rte_unused,
+							bool is_inner)
+{
+	const struct rte_flow_item_geneve *geneve_spec;
+	const struct rte_flow_item_geneve *geneve_mask;
+	struct sxe2_flow_pattern *pattern = is_inner ? &flow->pattern_inner : &flow->pattern_outer;
+	int32_t ret = 0;
+	geneve_spec = item->spec;
+	geneve_mask = item->mask;
+
+	if (!(geneve_spec && geneve_mask))
+		goto l_end;
+
+	if (geneve_mask->protocol || geneve_mask->ver_opt_len_o_c_rsvd0 || geneve_mask->rsvd1) {
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM,
+				item, "Unsupported some Geneve mask");
+		PMD_LOG_ERR(DRV, "Unsupported some Geneve mask");
+		ret = -EINVAL;
+		goto l_end;
+	}
+	if (geneve_mask->vni[0] || geneve_mask->vni[1] || geneve_mask->vni[2]) {
+		if (strcmp((const char *)geneve_mask->vni, "\xFF\xFF\xFF") != 0)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_GENEVE_VNI, pattern->map_mask);
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_GENEVE_VNI, pattern->map_spec);
+		pattern->item_spec.geneve.vni = (geneve_spec->vni[2] << 16) |
+			(geneve_spec->vni[1] << 8) | geneve_spec->vni[0];
+		pattern->item_mask.geneve.vni = (geneve_mask->vni[2] << 16) |
+			(geneve_mask->vni[1] << 8) | geneve_mask->vni[0];
+	}
+l_end:
+	sxe2_set_bit(SXE2_FLOW_HDR_GENEVE, pattern->hdrs);
+	return ret;
+}
+
+static int32_t sxe2_flow_parse_pattern_gtpu(const struct rte_flow_item *item,
+						struct rte_flow_error *error,
+						struct sxe2_flow *flow,
+						enum sxe2_expansion next __rte_unused,
+						bool is_inner)
+{
+	const struct rte_flow_item_gtp *gtpu_spec;
+	const struct rte_flow_item_gtp *gtpu_mask;
+	struct sxe2_flow_pattern *pattern = is_inner ? &flow->pattern_inner : &flow->pattern_outer;
+	int32_t ret = 0;
+	gtpu_spec = item->spec;
+	gtpu_mask = item->mask;
+
+	if (gtpu_mask == NULL && gtpu_spec == NULL)
+		goto l_end;
+
+	if (gtpu_mask->v_pt_rsv_flags || gtpu_mask->msg_type || gtpu_mask->msg_len) {
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM,
+				item, "Unsupported some GTPU mask");
+		PMD_LOG_ERR(DRV, "Unsupported some GTPU mask");
+		ret = -EINVAL;
+		goto l_end;
+	}
+	if (gtpu_mask->teid) {
+		if (gtpu_mask->teid != UINT32_MAX)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_GTPU_TEID, pattern->map_mask);
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_GTPU_TEID, pattern->map_spec);
+		pattern->item_spec.gtpu.teid = gtpu_spec->teid;
+		pattern->item_mask.gtpu.teid = gtpu_mask->teid;
+	}
+l_end:
+	sxe2_set_bit(SXE2_FLOW_HDR_GTPU, pattern->hdrs);
+	return ret;
+}
+
+static int32_t sxe2_flow_parse_pattern_gre(const struct rte_flow_item *item,
+					struct rte_flow_error *error,
+					struct sxe2_flow *flow,
+					enum sxe2_expansion next __rte_unused,
+					bool is_inner)
+{
+	const struct rte_flow_item_gre *gre_spec;
+	const struct rte_flow_item_gre *gre_mask;
+	struct sxe2_flow_pattern *pattern = is_inner ? &flow->pattern_inner : &flow->pattern_outer;
+	int32_t ret = 0;
+	gre_spec = item->spec;
+	gre_mask = item->mask;
+
+	if (gre_mask == NULL && gre_spec == NULL)
+		goto l_end;
+
+	if (gre_mask->c_rsvd0_ver || gre_mask->protocol) {
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM,
+				item, "Unsupported some GRE mask");
+		PMD_LOG_ERR(DRV, "Unsupported some GRE mask");
+		ret = -EINVAL;
+		goto l_end;
+	}
+l_end:
+	sxe2_set_bit(SXE2_FLOW_HDR_GRE, pattern->hdrs);
+	return ret;
+}
+
+static int32_t sxe2_flow_parse_pattern_nvgre(const struct rte_flow_item *item,
+					struct rte_flow_error *error,
+					struct sxe2_flow *flow,
+					enum sxe2_expansion next __rte_unused,
+					bool is_inner)
+{
+	const struct rte_flow_item_nvgre *nvgre_spec;
+	const struct rte_flow_item_nvgre *nvgre_mask;
+	struct sxe2_flow_pattern *pattern = is_inner ? &flow->pattern_inner : &flow->pattern_outer;
+	int32_t ret = 0;
+	nvgre_spec = item->spec;
+	nvgre_mask = item->mask;
+
+	if (nvgre_mask == NULL && nvgre_spec == NULL)
+		goto l_end;
+
+	if (nvgre_mask->c_k_s_rsvd0_ver || nvgre_mask->protocol || nvgre_mask->flow_id) {
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM,
+				item, "Unsupported some NVGRE mask");
+		PMD_LOG_ERR(DRV, "Unsupported some NVGRE mask");
+		ret = -EINVAL;
+		goto l_end;
+	}
+	if (nvgre_mask->tni[0] || nvgre_mask->tni[1] || nvgre_mask->tni[2]) {
+		if (strcmp((const char *)nvgre_mask->tni, "\xFF\xFF\xFF") != 0)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_NVGRE_TNI, pattern->map_mask);
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_NVGRE_TNI, pattern->map_spec);
+		pattern->item_spec.nvgre.tni =
+			(nvgre_spec->tni[2] << 16) |
+			(nvgre_spec->tni[1] << 8) |
+			(nvgre_spec->tni[0]);
+		pattern->item_mask.nvgre.tni =
+			(nvgre_mask->tni[2] << 16) |
+			(nvgre_mask->tni[1] << 8) |
+			(nvgre_mask->tni[0]);
+	}
+
+l_end:
+	sxe2_set_bit(SXE2_FLOW_HDR_GRE, pattern->hdrs);
+	return ret;
+}
+
+static int32_t sxe2_flow_parse_pattern_vxlan(const struct rte_flow_item *item,
+					struct rte_flow_error *error,
+					struct sxe2_flow *flow,
+					enum sxe2_expansion next __rte_unused,
+					bool is_inner)
+{
+	const struct rte_flow_item_vxlan *vxlan_spec;
+	const struct rte_flow_item_vxlan *vxlan_mask;
+	struct sxe2_flow_pattern *pattern = is_inner ? &flow->pattern_inner : &flow->pattern_outer;
+	int32_t ret = 0;
+	vxlan_spec = item->spec;
+	vxlan_mask = item->mask;
+
+	if (vxlan_mask == NULL && vxlan_spec == NULL)
+		goto l_end;
+
+	if (vxlan_mask->flags ||
+	    vxlan_mask->rsvd1 ||
+	    vxlan_mask->rsvd0[0] ||
+	    vxlan_mask->rsvd0[1] ||
+	    vxlan_mask->rsvd0[2]) {
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM,
+				item, "Unsupported some VXLAN mask");
+		PMD_LOG_ERR(DRV, "Unsupported some VXLAN mask");
+		ret = -EINVAL;
+		goto l_end;
+	}
+	if (vxlan_mask->vni[0] || vxlan_mask->vni[1] || vxlan_mask->vni[2]) {
+		if (strcmp((const char *)vxlan_mask->vni, "\xFF\xFF\xFF") != 0)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_VXLAN_VNI, pattern->map_mask);
+
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_VXLAN_VNI, pattern->map_spec);
+		pattern->item_spec.vxlan.vni =
+			(vxlan_spec->vni[2] << 16) |
+			(vxlan_spec->vni[1] << 8) |
+			(vxlan_spec->vni[0]);
+		pattern->item_mask.vxlan.vni =
+			(vxlan_mask->vni[2] << 16) |
+			(vxlan_mask->vni[1] << 8) |
+			(vxlan_mask->vni[0]);
+	}
+l_end:
+	sxe2_set_bit(SXE2_FLOW_HDR_VXLAN, pattern->hdrs);
+	return ret;
+}
+
+static int32_t sxe2_flow_parse_pattern_vxlan_gpe(const struct rte_flow_item *item,
+					     struct rte_flow_error *error,
+					     struct sxe2_flow *flow,
+					     enum sxe2_expansion next __rte_unused,
+					     bool is_inner)
+{
+	const struct rte_flow_item_vxlan_gpe *vxlan_gpe_spec;
+	const struct rte_flow_item_vxlan_gpe *vxlan_gpe_mask;
+	struct sxe2_flow_pattern *pattern = is_inner ? &flow->pattern_inner : &flow->pattern_outer;
+	int32_t ret = 0;
+	vxlan_gpe_spec = item->spec;
+	vxlan_gpe_mask = item->mask;
+
+	if (vxlan_gpe_mask == NULL && vxlan_gpe_spec == NULL)
+		goto l_end;
+
+	if (vxlan_gpe_mask->flags ||
+	    vxlan_gpe_mask->protocol ||
+	    vxlan_gpe_mask->rsvd1 ||
+	    vxlan_gpe_mask->rsvd0[0] ||
+	    vxlan_gpe_mask->rsvd0[1]) {
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM,
+				item, "Unsupported some VXLAN-GPE mask");
+		PMD_LOG_ERR(DRV, "Unsupported some VXLAN-GPE mask");
+		ret = -EINVAL;
+		goto l_end;
+	}
+	if (vxlan_gpe_mask->vni[0] || vxlan_gpe_mask->vni[1] || vxlan_gpe_mask->vni[2]) {
+		if (strcmp((const char *)vxlan_gpe_mask->vni, "\xFF\xFF\xFF") != 0)
+			sxe2_set_bit(SXE2_FLOW_FLD_ID_VXLAN_VNI, pattern->map_mask);
+
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_VXLAN_VNI, pattern->map_spec);
+		pattern->item_spec.vxlan.vni =
+			(vxlan_gpe_spec->vni[2] << 16) |
+			(vxlan_gpe_spec->vni[1] << 8) |
+			(vxlan_gpe_spec->vni[0]);
+		pattern->item_mask.vxlan.vni =
+			(vxlan_gpe_mask->vni[2] << 16) |
+			(vxlan_gpe_mask->vni[1] << 8) |
+			(vxlan_gpe_mask->vni[0]);
+	}
+
+l_end:
+	sxe2_set_bit(SXE2_FLOW_HDR_VXLAN, pattern->hdrs);
+	return ret;
+}
+
+struct sxe2_flow_parse_pattern_ops sxe2_flow_parse_pattern_list[] = {
+	[SXE2_EXPANSION_OUTER_ETH] = {
+		.is_inner = false,
+		.func = sxe2_flow_parse_pattern_eth,
+	},
+	[SXE2_EXPANSION_OUTER_VLAN] = {
+		.is_inner = false,
+		.func = sxe2_flow_parse_pattern_vlan,
+	},
+	[SXE2_EXPANSION_OUTER_QINQ] = {
+		.is_inner = false,
+		.func = sxe2_flow_parse_pattern_vlan,
+	},
+	[SXE2_EXPANSION_OUTER_IPV4] = {
+		.is_inner = false,
+		.func = sxe2_flow_parse_pattern_ipv4,
+	},
+	[SXE2_EXPANSION_OUTER_IPV6] = {
+		.is_inner = false,
+		.func = sxe2_flow_parse_pattern_ipv6,
+	},
+	[SXE2_EXPANSION_OUTER_IPV6_FRAG_EXT] = {
+		.is_inner = false,
+		.func = sxe2_flow_parse_pattern_ipv6_frag_ext,
+	},
+	[SXE2_EXPANSION_OUTER_TCP] = {
+		.is_inner = false,
+		.func = sxe2_flow_parse_pattern_tcp,
+	},
+	[SXE2_EXPANSION_OUTER_UDP] = {
+		.is_inner = false,
+		.func = sxe2_flow_parse_pattern_udp,
+	},
+	[SXE2_EXPANSION_OUTER_SCTP] = {
+		.is_inner = false,
+		.func = sxe2_flow_parse_pattern_sctp,
+	},
+	[SXE2_EXPANSION_GENEVE] = {
+		.is_inner = false,
+		.func = sxe2_flow_parse_pattern_geneve,
+	},
+	[SXE2_EXPANSION_GTPU] = {
+		.is_inner = false,
+		.func = sxe2_flow_parse_pattern_gtpu,
+	},
+	[SXE2_EXPANSION_GRE] = {
+		.is_inner = false,
+		.func = sxe2_flow_parse_pattern_gre,
+	},
+	[SXE2_EXPANSION_NVGRE] = {
+		.is_inner = false,
+		.func = sxe2_flow_parse_pattern_nvgre,
+	},
+	[SXE2_EXPANSION_VXLAN] = {
+		.is_inner = false,
+		.func = sxe2_flow_parse_pattern_vxlan,
+	},
+	[SXE2_EXPANSION_VXLAN_GPE] = {
+		.is_inner = false,
+		.func = sxe2_flow_parse_pattern_vxlan_gpe,
+	},
+	[SXE2_EXPANSION_ETH] = {
+		.is_inner = true,
+		.func = sxe2_flow_parse_pattern_eth,
+	},
+	[SXE2_EXPANSION_VLAN] = {
+		.is_inner = true,
+		.func = sxe2_flow_parse_pattern_vlan,
+	},
+	[SXE2_EXPANSION_IPV4] = {
+		.is_inner = true,
+		.func = sxe2_flow_parse_pattern_ipv4,
+	},
+	[SXE2_EXPANSION_IPV6] = {
+		.is_inner = true,
+		.func = sxe2_flow_parse_pattern_ipv6,
+	},
+	[SXE2_EXPANSION_IPV6_FRAG_EXT] = {
+		.is_inner = true,
+		.func = sxe2_flow_parse_pattern_ipv6_frag_ext,
+	},
+	[SXE2_EXPANSION_TCP] = {
+		.is_inner = true,
+		.func = sxe2_flow_parse_pattern_tcp,
+	},
+	[SXE2_EXPANSION_UDP] = {
+		.is_inner = true,
+		.func = sxe2_flow_parse_pattern_udp,
+	},
+	[SXE2_EXPANSION_SCTP] = {
+		.is_inner = true,
+		.func = sxe2_flow_parse_pattern_sctp,
+	},
+
+};
+
+int32_t sxe2_flow_parse_pattern(struct rte_eth_dev *dev __rte_unused,
+			    const struct rte_flow_item patterns[],
+			    struct rte_flow_error *error,
+			    struct sxe2_flow *flow)
+{
+	int32_t ret = 0;
+	const struct rte_flow_item *item = patterns;
+	enum sxe2_expansion now = SXE2_EXPANSION_OUTER_ETH;
+	enum sxe2_expansion next = SXE2_EXPANSION_OUTER_ETH;
+	enum sxe2_flow_tunnel_type tunnel_type = SXE2_FLOW_TUNNEL_TYPE_NONE;
+	DECLARE_BITMAP(flow_type, SXE2_EXPANSION_MAX);
+	sxe2_bitmap_zero(flow_type, SXE2_EXPANSION_MAX);
+	sxe2_flow_parse_pattern_func_t func;
+	bool is_inner = false;
+
+	for (item = patterns; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
+		if (item->type == RTE_FLOW_ITEM_TYPE_VOID)
+			continue;
+
+		if (item->last) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM, item,
+					"FANV not support range pattern.");
+			PMD_LOG_ERR(DRV, "flow not supported range pattern.");
+			ret = -EINVAL;
+			goto l_end;
+		}
+		ret = sxe2_flow_is_expandable_item(item);
+		if (ret != 0) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM, item,
+					"Unsupported item type.");
+			PMD_LOG_ERR(DRV, "Unsupported item type: %d", item->type);
+			goto l_end;
+		}
+		next = now;
+		ret = sxe2_flow_valid_next_expansion(&next, item,
+				&tunnel_type, flow_type);
+		if (ret != 0) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM, item,
+					"Invalid pattern sequence for rule.");
+			PMD_LOG_ERR(DRV, "Invalid pattern sequence for rule.");
+			goto l_end;
+		}
+
+		if ((item->spec != NULL && item->mask == NULL) ||
+		    (item->spec == NULL && item->mask != NULL)) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM, item,
+					"Invalid pattern spec miss macth mask for rule.");
+			PMD_LOG_ERR(DRV, "Invalid pattern spec miss macth mask for rule.");
+			goto l_end;
+		}
+
+		func = sxe2_flow_parse_pattern_list[now].func;
+		is_inner = sxe2_flow_parse_pattern_list[now].is_inner;
+		ret = func(item, error, flow, next, is_inner);
+		if (ret != 0)
+			goto l_end;
+		now = next;
+	}
+	if (sxe2_bitmap_weight(flow->pattern_outer.hdrs, SXE2_FLOW_HDR_MAX) > 0)
+		flow->has_hdr = 1;
+
+	if (sxe2_bitmap_weight(flow->pattern_inner.map_mask, SXE2_FLOW_FLD_ID_MAX) > 0 ||
+	   sxe2_bitmap_weight(flow->pattern_outer.map_mask, SXE2_FLOW_FLD_ID_MAX) > 0)
+		flow->has_mask = 1;
+
+	if (sxe2_bitmap_weight(flow->pattern_inner.map_spec, SXE2_FLOW_FLD_ID_MAX) > 0 ||
+	   sxe2_bitmap_weight(flow->pattern_outer.map_spec, SXE2_FLOW_FLD_ID_MAX) > 0)
+		flow->has_spec = 1;
+
+	flow->meta.tunnel_type = tunnel_type;
+	if (flow->has_hdr) {
+		ret = sxe2_flow_get_flow_type(flow);
+		if (ret != 0) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM,
+					NULL, "Unsupported flow type.");
+			goto l_end;
+		}
+	}
+	sxe2_bitmap_copy(flow->flow_type, flow_type, SXE2_EXPANSION_MAX);
+l_end:
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_flow_parse_pattern.h b/drivers/net/sxe2/sxe2_flow_parse_pattern.h
new file mode 100644
index 0000000000..69d83a6ea6
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_flow_parse_pattern.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef SXE2_FLOW_PARSE_PATTERN_H_
+#define SXE2_FLOW_PARSE_PATTERN_H_
+#include <rte_flow_driver.h>
+#include "sxe2_osal.h"
+#include "sxe2_flow_define.h"
+
+#define SXE2_FLOW_EXPAND_NEXT(...) \
+	((const enum sxe2_expansion []){ \
+		__VA_ARGS__, 0, \
+	})
+
+struct sxe2_flow_expand_node {
+	const enum rte_flow_item_type type;
+	const enum sxe2_flow_tunnel_type tunnel_type;
+	const uint8_t is_tunnel;
+	const enum sxe2_expansion *const next;
+	const char *const name;
+};
+
+typedef int32_t (*sxe2_flow_parse_pattern_func_t)(const struct rte_flow_item *item,
+					      struct rte_flow_error *error,
+					      struct sxe2_flow *flow,
+					      enum sxe2_expansion next,
+					      bool is_inner);
+
+struct sxe2_flow_parse_pattern_ops {
+	bool is_inner;
+	sxe2_flow_parse_pattern_func_t func;
+};
+
+int32_t sxe2_flow_parse_pattern(struct rte_eth_dev *dev,
+			    const struct rte_flow_item patterns[],
+			    struct rte_flow_error *error,
+			    struct sxe2_flow *flow);
+
+#endif /* SXE2_FLOW_PARSE_PATTERN_H_ */
diff --git a/drivers/net/sxe2/sxe2_irq.c b/drivers/net/sxe2/sxe2_irq.c
index 6dab2f8cd8..d8e0b19463 100644
--- a/drivers/net/sxe2/sxe2_irq.c
+++ b/drivers/net/sxe2/sxe2_irq.c
@@ -20,6 +20,7 @@
 #include "sxe2vf_regs.h"
 #include "sxe2_host_regs.h"
 #include "sxe2_cmd_chnl.h"
+#include "sxe2_switchdev.h"
 
 #define SXE2_INT_EVENT_OICR_ALL (SXE2_PF_INT_OICR_SWINT | \
 					SXE2_PF_INT_OICR_LAN_TX_ERR | \
@@ -59,6 +60,14 @@ static void sxe2_event_irq_common_handler(struct sxe2_adapter *adapter, uint64_t
 						     RTE_ETH_EVENT_INTR_LSC,
 						     NULL);
 	}
+	if (oicr & RTE_BIT32(SXE2_COM_SW_MODE_SWITCHDEV)) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "event notify switchdev");
+		(void)sxe2_switchdev_notify_callback(adapter, true);
+	}
+	if (oicr & RTE_BIT32(SXE2_COM_SW_MODE_LEGACY)) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "event notify legacy");
+		(void)sxe2_switchdev_notify_callback(adapter, false);
+	}
 }
 
 static uint32_t sxe2_event_intr_handle(void *param __rte_unused)
@@ -882,6 +891,42 @@ int32_t sxe2_rxq_intr_enable(struct rte_eth_dev *dev)
 	return ret;
 }
 
+int32_t sxe2_repr_rxq_intr_enable(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	uint16_t rxq_cnt = dev->data->nb_rx_queues;
+	int32_t ret = 0;
+	uint16_t qid = adapter->repr_priv_data->repr_q_id;
+	uint32_t val;
+
+	if (!rxq_cnt)
+		goto l_end;
+
+	sxe2_pci_hw_irq_disable(adapter, qid);
+	sxe2_pci_hw_int_itr_set(adapter, qid, SXE2_ITR_INTERVAL_NORMAL);
+	ret = sxe2_drv_rxq_bind_irq(adapter, qid, qid);
+	if (ret != 0) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "RXQ[%u] bind IRQ[%u] failed.",
+				qid, qid);
+		goto l_end;
+	}
+	sxe2_pci_hw_irq_enable(adapter, qid);
+
+	val = sxe2_pci_hw_irq_dyn_ctl_read(adapter, qid);
+	if ((val & SXE2VF_DYN_CTL_INTENABLE) == 0)
+		goto l_end;
+
+	sxe2_pci_hw_msix_disable(adapter, qid);
+	sxe2_pci_hw_irq_trigger(adapter, qid);
+	val = sxe2_pci_hw_irq_dyn_ctl_read(adapter, qid);
+	sxe2_pci_hw_irq_clear_pba(adapter, qid);
+	val = sxe2_pci_hw_irq_dyn_ctl_read(adapter, qid);
+	sxe2_pci_hw_msix_enable(adapter, qid);
+
+l_end:
+	return ret;
+}
+
 void sxe2_rxq_intr_disable(struct rte_eth_dev *dev)
 {
 	struct sxe2_adapter *adapter =
@@ -902,6 +947,15 @@ void sxe2_rxq_intr_disable(struct rte_eth_dev *dev)
 	return;
 }
 
+void sxe2_repr_rxq_intr_disable(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	uint16_t qid = adapter->repr_priv_data->repr_q_id;
+
+	sxe2_pci_hw_irq_disable(adapter, qid);
+	(void)sxe2_drv_rxq_unbind_irq(adapter, qid);
+}
+
 int32_t sxe2_rx_queue_intr_enable(struct rte_eth_dev *dev, uint16_t queue_id)
 {
 	struct sxe2_adapter *adapter =
diff --git a/drivers/net/sxe2/sxe2_irq.h b/drivers/net/sxe2/sxe2_irq.h
index 31216240e6..b56c2664b8 100644
--- a/drivers/net/sxe2/sxe2_irq.h
+++ b/drivers/net/sxe2/sxe2_irq.h
@@ -60,8 +60,12 @@ void sxe2_sw_irq_ctx_hw_cap_set(struct sxe2_adapter *adapter,
 
 int32_t sxe2_rxq_intr_enable(struct rte_eth_dev *dev);
 
+int32_t sxe2_repr_rxq_intr_enable(struct rte_eth_dev *dev);
+
 void sxe2_rxq_intr_disable(struct rte_eth_dev *dev);
 
+void sxe2_repr_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);
diff --git a/drivers/net/sxe2/sxe2_queue.c b/drivers/net/sxe2/sxe2_queue.c
index 220cab6fce..afb2681b72 100644
--- a/drivers/net/sxe2/sxe2_queue.c
+++ b/drivers/net/sxe2/sxe2_queue.c
@@ -24,7 +24,11 @@ int32_t sxe2_queues_init(struct rte_eth_dev *dev)
 	struct sxe2_rx_queue *rxq;
 	uint16_t nb_rxq;
 
-	frame_size = dev->data->mtu + SXE2_ETH_OVERHEAD;
+	if (adapter->is_dev_repr)
+		frame_size = SXE2_FRAME_SIZE_MAX - SXE2_ETH_OVERHEAD;
+	else
+		frame_size = dev->data->mtu + SXE2_ETH_OVERHEAD;
+
 	for (nb_rxq = 0; nb_rxq < dev->data->nb_rx_queues; nb_rxq++) {
 		rxq = dev->data->rx_queues[nb_rxq];
 		if (!rxq)
diff --git a/drivers/net/sxe2/sxe2_stats.c b/drivers/net/sxe2/sxe2_stats.c
index e1ba7e3958..3ad8fe2fe9 100644
--- a/drivers/net/sxe2/sxe2_stats.c
+++ b/drivers/net/sxe2/sxe2_stats.c
@@ -154,7 +154,12 @@ static int32_t sxe2_vsi_hw_stats_get_update(struct sxe2_adapter *adapter)
 
 	ret = sxe2_drv_get_vsi_stats(adapter);
 	if (ret) {
-		PMD_LOG_ERR(DRV, "get vsi stats failed, ret:%d.", ret);
+		if (adapter->is_dev_repr) {
+			PMD_LOG_WARN(DRV, "get repr vsi stats failed, ret:%d.", ret);
+			ret = 0;
+		} else {
+			PMD_LOG_ERR(DRV, "get vsi stats failed, ret:%d.", ret);
+		}
 		goto l_end;
 	}
 
@@ -226,7 +231,7 @@ static void sxe2_stats_update(struct sxe2_adapter *adapter)
 	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) {
+	if (adapter->dev_type != SXE2_DEV_T_VF  && !adapter->is_dev_repr) {
 		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;
@@ -359,7 +364,7 @@ int32_t sxe2_xstats_info_get(struct rte_eth_dev *dev,
 	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)
+	if (adapter->dev_type == SXE2_DEV_T_VF || adapter->is_dev_repr)
 		xstats_cnt = SXE2_XSTAT_CNT_VF;
 	else
 		xstats_cnt = SXE2_XSTAT_CNT_PF;
@@ -382,7 +387,7 @@ int32_t sxe2_xstats_info_get(struct rte_eth_dev *dev,
 		goto end;
 	}
 
-	if (adapter->dev_type == SXE2_DEV_T_VF) {
+	if (adapter->dev_type == SXE2_DEV_T_VF || adapter->is_dev_repr) {
 		sxe2_stats_update(adapter);
 		for (i = 0; i < xstats_cnt; i++) {
 			(void)sxe2_xstat_vf_offset_get(i, &offset);
@@ -426,7 +431,7 @@ int32_t sxe2_xstats_names_get(__rte_unused struct rte_eth_dev *dev,
 	int32_t ret = -1;
 	uint32_t xstats_cnt = 0;
 
-	if (adapter->dev_type == SXE2_DEV_T_VF) {
+	if (adapter->dev_type == SXE2_DEV_T_VF || adapter->is_dev_repr) {
 		field = sxe2_xstats_field_vf;
 		xstats_cnt = SXE2_XSTAT_CNT_VF;
 	} else {
@@ -471,7 +476,7 @@ int32_t sxe2_stats_hw_reset(struct rte_eth_dev *dev)
 		PMD_LOG_ERR(DRV, "reset vsi stats failed, ret:%d.", ret);
 		goto l_end;
 	}
-	if (adapter->dev_type != SXE2_DEV_T_VF) {
+	if (adapter->dev_type != SXE2_DEV_T_VF && !adapter->is_dev_repr) {
 		ret = sxe2_drv_mac_stats_reset(adapter);
 		if (ret) {
 			PMD_LOG_ERR(DRV, "reset mac stats failed, ret:%d.", ret);
diff --git a/drivers/net/sxe2/sxe2_switchdev.c b/drivers/net/sxe2/sxe2_switchdev.c
new file mode 100644
index 0000000000..44703cfb5c
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_switchdev.c
@@ -0,0 +1,332 @@
+/* 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_switchdev.h"
+#include "sxe2_cmd_chnl.h"
+#include "sxe2_host_regs.h"
+
+int32_t sxe2_uplink_clear(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+
+	if (!adapter->switchdev_info.is_switchdev) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "Current mode is not switchdev");
+		goto l_end;
+	}
+
+	ret = sxe2_drv_switchdev_uplink_config(adapter, false);
+l_end:
+	return ret;
+}
+
+int32_t sxe2_uplink_set(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+
+	if (!adapter->switchdev_info.is_switchdev) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "Current mode is not switchdev");
+		goto l_end;
+	}
+
+	ret = sxe2_drv_switchdev_uplink_config(adapter, true);
+l_end:
+	return ret;
+}
+
+int32_t sxe2_repr_clear(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+	uint16_t repr_id = 0;
+	struct rte_eth_dev *repr_dev;
+	struct sxe2_adapter *repr_adapter;
+	struct sxe2_switchdev_repr_info repr_vf;
+
+	if (!adapter->switchdev_info.is_switchdev) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "Current mode is not switchdev");
+		goto l_end;
+	}
+
+	for (repr_id = 0; repr_id < adapter->repr_ctxt.nb_repr_vf; repr_id++) {
+		repr_dev = adapter->repr_ctxt.vf_rep_eth_dev[repr_id];
+		if (!repr_dev)
+			continue;
+		repr_adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(repr_dev);
+		if (repr_adapter &&
+		    repr_adapter->repr_priv_data &&
+		    repr_adapter->repr_priv_data->cp_vsi) {
+			memset(&repr_vf, 0, sizeof(struct sxe2_switchdev_repr_info));
+
+			repr_vf.repr_pf_id = repr_adapter->repr_priv_data->repr_pf_id;
+			repr_vf.repr_vf_id = repr_adapter->repr_priv_data->repr_vf_id;
+			repr_vf.cp_vsi_id = repr_adapter->repr_priv_data->cp_vsi->vsi_id;
+			repr_vf.repr_q_id = repr_adapter->repr_priv_data->repr_q_id;
+			ret = sxe2_drv_switchdev_repr_vf_config(adapter, &repr_vf,
+								false);
+		}
+	}
+l_end:
+	return ret;
+}
+
+int32_t sxe2_repr_set(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+	uint16_t repr_id = 0;
+	struct rte_eth_dev *repr_dev;
+	struct sxe2_adapter *repr_adapter;
+	struct sxe2_switchdev_repr_info repr_vf;
+
+	if (!adapter->switchdev_info.is_switchdev) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "Current mode is not switchdev");
+		goto l_end;
+	}
+
+	for (repr_id = 0; repr_id < adapter->repr_ctxt.nb_repr_vf; repr_id++) {
+		repr_dev = adapter->repr_ctxt.vf_rep_eth_dev[repr_id];
+		if (!repr_dev)
+			continue;
+		repr_adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(repr_dev);
+		if (repr_adapter &&
+		    repr_adapter->repr_priv_data &&
+		    repr_adapter->repr_priv_data->cp_vsi) {
+			memset(&repr_vf, 0, sizeof(struct sxe2_switchdev_repr_info));
+
+			repr_vf.repr_pf_id = repr_adapter->repr_priv_data->repr_pf_id;
+			repr_vf.repr_vf_id = repr_adapter->repr_priv_data->repr_vf_id;
+			repr_vf.cp_vsi_id = repr_adapter->repr_priv_data->cp_vsi->vsi_id;
+			repr_vf.repr_q_id = repr_adapter->repr_priv_data->repr_q_id;
+			ret = sxe2_drv_switchdev_repr_vf_config(adapter, &repr_vf, true);
+		}
+	}
+
+l_end:
+	return ret;
+}
+
+void sxe2_free_repr_info(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	if (adapter->repr_ctxt.vf_rep_eth_dev) {
+		rte_free(adapter->repr_ctxt.vf_rep_eth_dev);
+		adapter->repr_ctxt.vf_rep_eth_dev = NULL;
+	}
+
+	adapter->repr_ctxt.nb_repr_vf = 0;
+}
+
+static int32_t sxe2_switchdev_clear(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+
+	if (!adapter->switchdev_info.is_switchdev) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "Current mode is not switchdev");
+		goto l_end;
+	}
+
+	adapter->switchdev_info.is_switchdev = false;
+
+	if (!adapter->flow_isolate_cfg && adapter->flow_isolated)
+		adapter->flow_isolated = false;
+
+	ret = sxe2_l2_rule_update(adapter);
+	if (ret != 0)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to update l2 rule");
+
+	ret = sxe2_switchdev_rule_update(adapter);
+	if (ret != 0)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to update switchdev rule");
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_switchdev_set(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+
+	if (adapter->switchdev_info.is_switchdev) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "Current mode switch dev");
+		goto l_end;
+	}
+
+	adapter->switchdev_info.is_switchdev = true;
+
+	if (adapter->flow_isolate_cfg && !adapter->flow_isolated)
+		adapter->flow_isolated = true;
+
+	ret = sxe2_l2_rule_update(adapter);
+	if (ret != 0)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to update l2 rule");
+
+	ret = sxe2_switchdev_rule_update(adapter);
+	if (ret != 0)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to update switchdev rule");
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_switchdev_notify_callback(struct sxe2_adapter *adapter, bool set)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[adapter->dev_info.dev_data->port_id];
+	int32_t ret = 0;
+	bool cur_switchdev_set = false;
+
+	if (adapter->repr_ctxt.nb_repr_vf) {
+		PMD_DEV_LOG_WARN(adapter, DRV, "switch dev notify remove dev");
+		rte_eth_dev_callback_process(dev, RTE_ETH_EVENT_INTR_RMV, NULL);
+		goto l_end;
+	}
+
+	ret = sxe2_drv_switchdev_mode_get(adapter, &cur_switchdev_set);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to get switchdev mode");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (set != cur_switchdev_set) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "current switchdev mode miss macth");
+		goto l_end;
+	}
+
+	if (set) {
+		ret = sxe2_switchdev_set(adapter);
+		if (ret != 0) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "Failed to set switchdev");
+			goto l_end;
+		}
+	} else {
+		ret = sxe2_switchdev_clear(adapter);
+		if (ret != 0) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "Failed to clear switchdev");
+			goto l_end;
+		}
+	}
+l_end:
+	return ret;
+}
+
+int32_t sxe2_switchdev_init(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	PMD_DEV_LOG_INFO(adapter, INIT, "switchdev init");
+
+	if (adapter->switchdev_info.is_switchdev)
+		adapter->flow_isolated = true;
+
+	adapter->repr_priv_data = NULL;
+	adapter->repr_ctxt.nb_repr_vf = 0;
+
+	return 0;
+}
+
+int32_t sxe2_switchdev_uninit(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	PMD_DEV_LOG_INFO(adapter, INIT, "switchdev uinit");
+
+	if (adapter->repr_priv_data) {
+		rte_free(adapter->repr_priv_data);
+		adapter->repr_priv_data = NULL;
+	}
+
+	return 0;
+}
+
+int32_t sxe2_switchdev_dev_info_init(struct rte_eth_dev *dev,
+			struct sxe2_adapter *parent_adapter)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_dev_info *dev_info = &adapter->dev_info;
+	struct sxe2_dev_info *parent_dev_info = &parent_adapter->dev_info;
+	struct sxe2_drv_dev_info_resp dev_info_resp = {0};
+	struct sxe2_drv_dev_fw_info_resp dev_fw_info_resp = {0};
+	int32_t ret = 0;
+
+	PMD_INIT_FUNC_TRACE();
+
+	dev_info->pci = parent_dev_info->pci;
+	dev_info->pci.max_vfs = 0;
+
+	ret = sxe2_drv_dev_info_get(adapter, &dev_info_resp);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to get device info, ret=[%d]", ret);
+		goto l_end;
+	}
+
+	ret = sxe2_drv_dev_fw_info_get(adapter, &dev_fw_info_resp);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to get device fw info, ret=[%d]", ret);
+		goto l_end;
+	}
+	dev_info->fw.build_id = dev_fw_info_resp.build_id;
+	dev_info->fw.fix_version_id = dev_fw_info_resp.fix_version_id;
+	dev_info->fw.sub_version_id = dev_fw_info_resp.sub_version_id;
+	dev_info->fw.main_version_id = dev_fw_info_resp.main_version_id;
+
+	rte_ether_addr_copy((struct rte_ether_addr *)dev_info_resp.mac_addr,
+						(struct rte_ether_addr *)dev_info->mac.perm_addr);
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_switchdev_repr_private_data_init(struct rte_eth_dev *dev,
+		struct sxe2_adapter *parent_adapter, uint16_t repr_id)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_repr_private_data *repr_priv_data = adapter->repr_priv_data;
+	int32_t ret = 0;
+
+	if (repr_priv_data != NULL)
+		goto l_end;
+
+	repr_priv_data = rte_zmalloc("sxe2_repr_priv_data",
+			sizeof(struct sxe2_repr_private_data), 0);
+	if (repr_priv_data == NULL) {
+		PMD_LOG_ERR(INIT, "Failed to malloc representor private data.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	repr_priv_data->parent_adapter = parent_adapter;
+	repr_priv_data->repr_id = repr_id;
+	repr_priv_data->cp_vsi =
+		TAILQ_FIRST(&parent_adapter->vsi_ctxt.other_vsi_list);
+	if (repr_priv_data->cp_vsi == NULL) {
+		PMD_LOG_ERR(INIT, "Failed to get cp vsi.");
+		ret = -EINVAL;
+		goto l_free;
+	}
+	repr_priv_data->repr_q_id = repr_id;
+	repr_priv_data->repr_pf_id = parent_adapter->pf_idx;
+	repr_priv_data->repr_vf_id = repr_id;
+	repr_priv_data->repr_vf_k_vsi_id =
+		parent_adapter->repr_ctxt.repr_vf_id[repr_id].kernel_vsi_id;
+	repr_priv_data->repr_vf_u_vsi_id =
+		parent_adapter->repr_ctxt.repr_vf_id[repr_id].dpdk_vsi_id;
+
+	repr_priv_data->repr_vf_vsi_id =
+		parent_adapter->repr_ctxt.repr_vf_id[repr_id].kernel_vsi_id !=
+		SXE2_INVALID_VSI_ID ?
+		parent_adapter->repr_ctxt.repr_vf_id[repr_id].kernel_vsi_id :
+		parent_adapter->repr_ctxt.repr_vf_id[repr_id].dpdk_vsi_id;
+
+	adapter->repr_priv_data = repr_priv_data;
+	goto l_end;
+l_free:
+	rte_free(repr_priv_data);
+l_end:
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_switchdev.h b/drivers/net/sxe2/sxe2_switchdev.h
new file mode 100644
index 0000000000..0a74454424
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_switchdev.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_SWITCHDEV_H__
+#define __SXE2_SWITCHDEV_H__
+#include <ethdev_driver.h>
+
+struct sxe2_adapter;
+
+int32_t sxe2_uplink_clear(struct sxe2_adapter *adapter);
+
+int32_t sxe2_uplink_set(struct sxe2_adapter *adapter);
+
+int32_t sxe2_repr_clear(struct sxe2_adapter *adapter);
+
+int32_t sxe2_repr_set(struct sxe2_adapter *adapter);
+
+int32_t sxe2_switchdev_notify_callback(struct sxe2_adapter *adapter, bool set);
+
+int32_t sxe2_switchdev_init(struct rte_eth_dev *dev);
+
+int32_t sxe2_switchdev_uninit(struct rte_eth_dev *dev);
+
+void sxe2_free_repr_info(struct rte_eth_dev *dev);
+
+int32_t sxe2_switchdev_dev_info_init(struct rte_eth_dev *dev,
+			struct sxe2_adapter *parent_adapter);
+
+int32_t sxe2_switchdev_repr_private_data_init(struct rte_eth_dev *dev,
+		struct sxe2_adapter *parent_adapter, uint16_t repr_id);
+
+#endif /* __SXE2_SWITCHDEV_H__ */
diff --git a/drivers/net/sxe2/sxe2_txrx.c b/drivers/net/sxe2/sxe2_txrx.c
index 798863dad9..82b2e4fb7c 100644
--- a/drivers/net/sxe2/sxe2_txrx.c
+++ b/drivers/net/sxe2/sxe2_txrx.c
@@ -151,6 +151,13 @@ void sxe2_tx_mode_func_set(struct rte_eth_dev *dev)
 	uint32_t batch_flags = 0;
 
 	PMD_INIT_FUNC_TRACE();
+
+	if (adapter->is_dev_repr) {
+		dev->tx_pkt_prepare = sxe2_tx_pkts_prepare;
+		dev->tx_pkt_burst = sxe2_tx_pkts;
+		tx_mode_flags = 0;
+		return;
+	}
 	if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
 		tx_mode_flags = 0;
 		ret = sxe2_tx_vec_support_check(dev, &vec_flags);
diff --git a/drivers/net/sxe2/sxe2_txrx_poll.c b/drivers/net/sxe2/sxe2_txrx_poll.c
index dcb8faf4ed..a662bb4375 100644
--- a/drivers/net/sxe2/sxe2_txrx_poll.c
+++ b/drivers/net/sxe2/sxe2_txrx_poll.c
@@ -454,6 +454,14 @@ uint16_t sxe2_tx_pkts(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkt
 				desc_l2tag2 = tx_pkt->vlan_tci_outer;
 				desc_type_cmd_tso_mss |= SXE2_TX_CTXT_DESC_CMD_IL2TAG2_MASK;
 			}
+			if (unlikely(vsi->vsi_type == SXE2_VSI_T_DPDK_ESW)) {
+				desc_type_cmd_tso_mss |=
+					(SXE2_TX_CTXT_DESC_CMD_SWTCH_VSI <<
+					SXE2_TX_CTXT_DESC_CMD_SHIFT);
+				desc_type_cmd_tso_mss |=
+					((vsi->adapter->repr_priv_data->repr_vf_vsi_id & 0x3FFULL)
+					<< SXE2_TX_CTXT_DESC_VSI_SHIFT);
+			}
 
 			ctxt_desc->tunneling_params =
 				rte_cpu_to_le_32(desc_tunneling_params);
diff --git a/drivers/net/sxe2/sxe2_vsi.c b/drivers/net/sxe2/sxe2_vsi.c
index baaa20c02e..d29480b931 100644
--- a/drivers/net/sxe2/sxe2_vsi.c
+++ b/drivers/net/sxe2/sxe2_vsi.c
@@ -98,9 +98,15 @@ static struct sxe2_vsi *sxe2_vsi_node_create(struct sxe2_adapter *adapter,
 
 static void sxe2_vsi_node_free(struct sxe2_vsi *vsi)
 {
+	struct sxe2_adapter *adapter;
+
 	if (!vsi)
 		return;
 
+	adapter = vsi->adapter;
+	if (vsi->vsi_type == SXE2_VSI_T_ESW)
+		TAILQ_REMOVE(&adapter->vsi_ctxt.other_vsi_list, vsi, next);
+
 	rte_free(vsi);
 	vsi = NULL;
 }
@@ -174,10 +180,54 @@ static int32_t sxe2_main_vsi_create(struct sxe2_adapter *adapter)
 	return ret;
 }
 
+int32_t sxe2_other_vsi_create(struct sxe2_adapter *adapter, uint16_t cnt_vf)
+{
+	int32_t ret = 0;
+	struct sxe2_vsi *other_vsi = NULL;
+	uint16_t vsi_id = SXE2_INVALID_VSI_ID;
+
+	PMD_INIT_FUNC_TRACE();
+
+	other_vsi = sxe2_vsi_node_create(adapter, SXE2_INVALID_VSI_ID, SXE2_VSI_T_DPDK_ESW);
+	if (other_vsi == NULL) {
+		ret = -ENOMEM;
+		goto l_end;
+	}
+
+	ret = sxe2_drv_switchdev_cpvsi_get(adapter, &vsi_id);
+	if (ret) {
+		PMD_LOG_ERR(DRV, "Failed to query vsi from fw, ret=%d", ret);
+		goto l_free_vsi;
+	}
+
+	other_vsi->vsi_id = vsi_id;
+	other_vsi->vsi_type = SXE2_VSI_T_DPDK_ESW;
+
+	ret = sxe2_drv_vsi_info_get(adapter, other_vsi);
+	if (ret) {
+		PMD_LOG_ERR(DRV, "Failed to query vsi info from fw, ret=%d", ret);
+		goto l_free_vsi;
+	}
+
+	other_vsi->txqs.q_cnt = RTE_MIN(cnt_vf, other_vsi->txqs.q_cnt);
+	other_vsi->rxqs.q_cnt = RTE_MIN(cnt_vf, other_vsi->rxqs.q_cnt);
+	other_vsi->irqs.avail_cnt = RTE_MIN(cnt_vf, other_vsi->irqs.avail_cnt);
+
+	TAILQ_INSERT_TAIL(&adapter->vsi_ctxt.other_vsi_list, other_vsi, next);
+	goto l_end;
+
+l_free_vsi:
+	sxe2_vsi_node_free(other_vsi);
+l_end:
+	return ret;
+}
+
 int32_t sxe2_vsi_init(struct rte_eth_dev *dev)
 {
 	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
 	int32_t ret = 0;
+	uint16_t srcvsi_list[SXE2_SRCVSI_PRUNE_MAX_NUM];
+	uint16_t srcvsi_cnt;
 
 	PMD_INIT_FUNC_TRACE();
 
@@ -187,6 +237,18 @@ int32_t sxe2_vsi_init(struct rte_eth_dev *dev)
 		goto l_end;
 	}
 
+	if (adapter->vsi_ctxt.kernel_vsi_id != SXE2_INVALID_VSI_ID) {
+		srcvsi_list[0] = adapter->vsi_ctxt.kernel_vsi_id;
+		srcvsi_list[1] = adapter->vsi_ctxt.dpdk_vsi_id;
+		srcvsi_cnt = 2;
+		ret = sxe2_drv_srcvsi_prune_config(adapter, srcvsi_list, srcvsi_cnt, true);
+		if (ret) {
+			PMD_LOG_ERR(DRV, "Failed to set src vsi to fw, ret=%d", ret);
+			goto l_end;
+		}
+		PMD_LOG_DEBUG(DRV, "Successfully set src vsi");
+	}
+
 l_end:
 	return ret;
 }
@@ -194,21 +256,105 @@ int32_t sxe2_vsi_init(struct rte_eth_dev *dev)
 void sxe2_vsi_uninit(struct rte_eth_dev *dev)
 {
 	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_vsi *var, *tvar;
 	int32_t ret;
+	uint16_t srcvsi_list[SXE2_SRCVSI_PRUNE_MAX_NUM];
+	uint16_t srcvsi_cnt;
 
 	if (adapter->vsi_ctxt.main_vsi == NULL) {
 		PMD_LOG_INFO(DRV, "vsi is not created, no need to destroy.");
 		goto l_end;
 	}
 
+	if (adapter->vsi_ctxt.kernel_vsi_id != SXE2_INVALID_VSI_ID) {
+		srcvsi_list[0] = adapter->vsi_ctxt.kernel_vsi_id;
+		srcvsi_list[1] = adapter->vsi_ctxt.dpdk_vsi_id;
+		srcvsi_cnt = 2;
+		ret = sxe2_drv_srcvsi_prune_config(adapter, srcvsi_list,
+						   srcvsi_cnt, false);
+		if (ret) {
+			PMD_LOG_ERR(DRV, "Failed to clear src vsi to fw, ret=%d", ret);
+			if (ret == -EPERM)
+				goto l_free;
+			goto l_end;
+		}
+		PMD_LOG_DEBUG(DRV, "Successfully clear src vsi");
+	}
+
+l_free:
 	ret = sxe2_vsi_destroy(adapter, adapter->vsi_ctxt.main_vsi);
 	if (ret) {
 		PMD_LOG_ERR(DRV, "Failed to del vsi from fw, ret=%d", ret);
 		goto l_end;
 	}
+	RTE_TAILQ_FOREACH_SAFE(var, &adapter->vsi_ctxt.other_vsi_list, next, tvar) {
+		ret = sxe2_vsi_destroy(adapter, var);
+		if (ret) {
+			PMD_LOG_ERR(DRV, "Failed to del vsi from fw, ret=%d", ret);
+			break;
+		}
+	}
 
 	PMD_LOG_DEBUG(DRV, "vsi destroyed.");
 
 l_end:
 	return;
 }
+
+int32_t sxe2_vsi_repr_main_vsi_create(struct rte_eth_dev *dev,
+				  struct sxe2_adapter *parent_adapter,
+				  uint16_t repr_id)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_vsi *vsi = NULL;
+	struct sxe2_vsi *pos;
+	int32_t ret = 0;
+
+	TAILQ_FOREACH(pos, &parent_adapter->vsi_ctxt.other_vsi_list, next) {
+		if (pos->vsi_type == SXE2_VSI_T_DPDK_ESW) {
+			vsi = pos;
+			break;
+		}
+	}
+
+	if (vsi == NULL) {
+		PMD_LOG_ERR(INIT, "Failed to get dpdk vsi.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	TAILQ_INIT(&adapter->vsi_ctxt.other_vsi_list);
+
+	adapter->vsi_ctxt.vsi_type = SXE2_VSI_T_DPDK_ESW;
+	adapter->vsi_ctxt.kernel_vsi_id = SXE2_INVALID_VSI_ID;
+
+	adapter->cdev = parent_adapter->cdev;
+
+	adapter->q_ctxt.base_idx_in_pf = vsi->txqs.base_idx_in_func +
+		RTE_MIN(vsi->txqs.q_cnt, repr_id);
+	adapter->irq_ctxt.base_idx_in_func = vsi->irqs.base_idx_in_pf +
+		RTE_MIN(vsi->irqs.avail_cnt, repr_id);
+	adapter->q_ctxt.qp_cnt_assign = RTE_MIN(vsi->txqs.q_cnt, 1);
+	adapter->irq_ctxt.max_cnt_hw = RTE_MIN(vsi->irqs.avail_cnt, 1);
+
+	adapter->vsi_ctxt.main_vsi =
+		sxe2_vsi_node_create(adapter, vsi->vsi_id, SXE2_VSI_T_DPDK_ESW);
+	if (adapter->vsi_ctxt.main_vsi == NULL) {
+		ret = -ENOMEM;
+		PMD_LOG_ERR(DRV, "Failed to create vsi struct, ret=%d", ret);
+		goto l_end;
+	}
+	adapter->vsi_ctxt.dpdk_vsi_id = adapter->vsi_ctxt.main_vsi->vsi_id;
+
+	ret = 0;
+
+l_end:
+	return ret;
+}
+
+void sxe2_vsi_repr_main_vsi_destroy(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	sxe2_vsi_node_free(adapter->vsi_ctxt.main_vsi);
+}
diff --git a/drivers/net/sxe2/sxe2_vsi.h b/drivers/net/sxe2/sxe2_vsi.h
index 1d74c3262f..d4b2cd6554 100644
--- a/drivers/net/sxe2/sxe2_vsi.h
+++ b/drivers/net/sxe2/sxe2_vsi.h
@@ -193,13 +193,23 @@ struct sxe2_vsi_context {
 	uint16_t bond_member_dpdk_vsi_id[SXE2_MAX_BOND_MEMBER_CNT];
 
 	struct sxe2_vsi *main_vsi;
+
+	struct sxe2_vsi_list_head other_vsi_list;
 };
 
 void sxe2_sw_vsi_ctx_hw_cap_set(struct sxe2_adapter *adapter,
-		struct sxe2_drv_vsi_caps *vsi_caps);
+				struct sxe2_drv_vsi_caps *vsi_caps);
+
+int32_t sxe2_other_vsi_create(struct sxe2_adapter *adapter,  uint16_t cnt_vf);
 
 int32_t sxe2_vsi_init(struct rte_eth_dev *dev);
 
 void sxe2_vsi_uninit(struct rte_eth_dev *dev);
 
+int32_t sxe2_vsi_repr_main_vsi_create(struct rte_eth_dev *dev,
+				  struct sxe2_adapter *parent_adapter,
+				  uint16_t repr_id);
+
+void sxe2_vsi_repr_main_vsi_destroy(struct rte_eth_dev *dev);
+
 #endif /* SXE2_VSI_H */
-- 
2.52.0


^ permalink raw reply related

* [PATCH v5 12/23] net/sxe2: add NEON vec Rx/Tx burst functions
From: liujie5 @ 2026-06-22  9:26 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260622092324.3091185-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   | 689 ++++++++++++++++++++++++
 5 files changed, 742 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 00307f2fcc..798863dad9 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 f004970949..9ac99cf0fa 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..4e5cb87cd5
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_txrx_vec_neon.c
@@ -0,0 +1,689 @@
+/* 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)
+{
+	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 = sxe2_ptype_tbl[vgetq_lane_u16(ptype_all, 3)];
+	rx_pkts[2]->packet_type = sxe2_ptype_tbl[vgetq_lane_u16(ptype_all, 7)];
+	rx_pkts[1]->packet_type = sxe2_ptype_tbl[vgetq_lane_u16(ptype_all, 1)];
+	rx_pkts[0]->packet_type = sxe2_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;
+
+	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]);
+
+		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)
+{
+	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;
+
+	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);
+
+	if (rx_done_num == 0)
+		goto l_end;
+
+	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


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