From: Jie Liu <[email protected]>

Introduce private testpmd commands and implementation files to enable
debugging and testing of sxe2-specific hardware features (such as
packet scheduling reset, UDP tunnel configuration, and IPsec ingress/
egress offloads) directly within the testpmd application.

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

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.

This commit introduces sxe2_memseg_walk_cb() as a helper that walks
memory segments using the thread-unsafe variant
rte_memseg_walk_thread_unsafe(), which is safe to call from
memory-related callbacks [citation:1][citation:3][citation:5].

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

Signed-off-by: Jie Liu <[email protected]>
---
 drivers/common/sxe2/sxe2_common.c     | 110 +++
 drivers/common/sxe2/sxe2_common.h     |   2 +
 drivers/common/sxe2/sxe2_ioctl_chnl.c |   2 +-
 drivers/net/sxe2/meson.build          |   5 +-
 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        | 287 +++++++-
 drivers/net/sxe2/sxe2_ethdev.h        |   8 +
 drivers/net/sxe2/sxe2_irq.c           |  29 +
 drivers/net/sxe2/sxe2_rx.c            |  12 +
 drivers/net/sxe2/sxe2_testpmd.c       | 733 +++++++++++++++++++
 drivers/net/sxe2/sxe2_testpmd_lib.c   | 969 ++++++++++++++++++++++++++
 drivers/net/sxe2/sxe2_testpmd_lib.h   | 142 ++++
 drivers/net/sxe2/sxe2_tm.c            |  18 +
 drivers/net/sxe2/sxe2_tm.h            |   2 +
 17 files changed, 2371 insertions(+), 4 deletions(-)
 create mode 100644 drivers/net/sxe2/sxe2_testpmd.c
 create mode 100644 drivers/net/sxe2/sxe2_testpmd_lib.c
 create mode 100644 drivers/net/sxe2/sxe2_testpmd_lib.h

diff --git a/drivers/common/sxe2/sxe2_common.c 
b/drivers/common/sxe2/sxe2_common.c
index a5d36998e1..38ce47bfbd 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=%" PRIu64 " 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)
diff --git a/drivers/net/sxe2/meson.build b/drivers/net/sxe2/meson.build
index 4fb2333926..04369402b7 100644
--- a/drivers/net/sxe2/meson.build
+++ b/drivers/net/sxe2/meson.build
@@ -9,9 +9,10 @@ endif
 
 cflags += ['-g']
 
-deps += ['common_sxe2', 'hash','cryptodev','security']
+deps += ['common_sxe2', 'hash', 'cryptodev', 'security', 'cmdline']
 
 includes += include_directories('../../common/sxe2')
+testpmd_sources = files('sxe2_testpmd.c')
 
 if arch_subdir == 'x86'
         sources += files('sxe2_txrx_vec_sse.c')
@@ -79,5 +80,5 @@ sources += files(
         'sxe2_flow_parse_engine.c',
         'sxe2_dump.c',
         'sxe2_txrx_check_mbuf.c',
-
+        'sxe2_testpmd_lib.c',
 )
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 bcb9fb4ff9..8aa443919b 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 9898456aea..e2fbb9a384 100644
--- a/drivers/net/sxe2/sxe2_dump.c
+++ b/drivers/net/sxe2/sxe2_dump.c
@@ -188,6 +188,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)
@@ -274,6 +288,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 73a92d99f8..702f84668c 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -68,7 +68,14 @@ 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_SW_STATS "drv-sw-stats"
+#define SXE2_DEVARG_HIGH_PERFORMANCE_MODE "high-performance-mode"
+#define SXE2_DEVARG_SCHED_LAYER_MODE "sched-layer-mode"
+#define SXE2_DEVARG_RX_LOW_LATENCY "rx-low-latency"
 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,
                                      .bar_idx = 0,
@@ -980,6 +987,149 @@ static inline void sxe2_init_ptype_tbl(struct rte_eth_dev 
*dev)
        sxe2_init_ptype_list(ptype);
 }
 
+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;
+       uint8_t fnav_stat_type = 0;
+       char *endptr = NULL;
+
+       if (value == NULL || args == NULL) {
+               ret = 0;
+               goto l_end;
+       }
+       errno = 0;
+       fnav_stat_type = (uint8_t)strtoul(value, &endptr, 10);
+       if (errno != 0 || *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 = 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;
+       uint8_t sched_layer_mode;
+       char *endptr = NULL;
+
+       if (value == NULL || args == NULL) {
+               ret = 0;
+               goto l_end;
+       }
+       errno = 0;
+       sched_layer_mode = (uint8_t)strtoul(value, &endptr, 10);
+       if (errno != 0 || *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 = sched_layer_mode;
+       ret = 0;
+l_end:
+       return ret;
+}
+static int32_t sxe2_parse_high_performance_mode(const char *key, const char 
*value, void *args)
+{
+       int32_t ret = -EINVAL;
+       uint8_t *num = (uint8_t *)args;
+       uint8_t high_performance_mode;
+       char *endptr = NULL;
+
+       if (value == NULL || args == NULL) {
+               ret = 0;
+               goto l_end;
+       }
+       errno = 0;
+       high_performance_mode = (uint8_t)strtoul(value, &endptr, 10);
+       if (errno != 0 || *endptr != '\0') {
+               PMD_LOG_WARN(INIT, "%s: \"%s\" is not a valid int value.",
+                       key, value);
+               goto l_end;
+       }
+       if (high_performance_mode != 1) {
+               PMD_LOG_ERR(INIT, "%s: \"%s\" != 1.",
+                       key, value);
+               goto l_end;
+       }
+       *num = high_performance_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 = 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;
+       uint8_t bool_val = 0;
+       char *endptr = NULL;
+
+       if (value == NULL || args == NULL) {
+               ret = 0;
+               goto l_end;
+       }
+       errno = 0;
+       bool_val = (uint8_t)strtoul(value, &endptr, 10);
+       if (errno != 0 || *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 = 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)
 {
@@ -1047,6 +1197,69 @@ 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();
+
+       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_SW_STATS,
+                                &sxe2_parse_bool,
+                                &adapter->devargs.sw_stats_en);
+       if (ret) {
+               PMD_DEV_LOG_ERR(adapter, INIT, "Failed to parse sw stats 
enable, ret:%d", ret);
+               goto l_end;
+       }
+       ret = sxe2_kvargs_process(kvargs, SXE2_DEVARG_HIGH_PERFORMANCE_MODE,
+                                &sxe2_parse_high_performance_mode,
+                                &adapter->devargs.high_performance_mode);
+       if (ret) {
+               PMD_DEV_LOG_ERR(adapter, INIT, "Failed to parse high 
performance, 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;
@@ -1599,6 +1812,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;
@@ -1661,6 +1905,32 @@ static int32_t sxe2_sched_uinit(struct rte_eth_dev *dev)
        return ret;
 }
 
+int32_t sxe2_sched_reset(struct rte_eth_dev *dev)
+{
+       int32_t ret = 0;
+
+       if (dev->data->dev_started) {
+               PMD_LOG_ERR(DRV, "Device failed to Stop.");
+               ret = -EPERM;
+               goto l_end;
+       }
+
+       ret = sxe2_tm_conf_reset(dev);
+       if (ret)
+               goto l_end;
+
+       ret = sxe2_sched_uinit(dev);
+       if (ret)
+               goto l_end;
+
+       ret = sxe2_sched_init(dev);
+       if (ret)
+               goto l_end;
+
+l_end:
+       return ret;
+}
+
 static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
                             struct sxe2_dev_kvargs_info *kvargs __rte_unused)
 {
@@ -1683,6 +1953,12 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 
        sxe2_init_ptype_tbl(dev);
 
+       ret = sxe2_args_parse(dev, kvargs);
+       if (ret) {
+               PMD_LOG_ERR(INIT, "Failed to parse devargs, ret=%d", ret);
+               goto l_end;
+       }
+
        ret = sxe2_hw_init(dev);
        if (ret) {
                PMD_LOG_ERR(INIT, "Failed to initialize hw, ret=[%d]", ret);
@@ -1749,6 +2025,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);
@@ -1772,6 +2054,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:
@@ -1817,6 +2101,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;
diff --git a/drivers/net/sxe2/sxe2_ethdev.h b/drivers/net/sxe2/sxe2_ethdev.h
index b103679c78..34550384e9 100644
--- a/drivers/net/sxe2/sxe2_ethdev.h
+++ b/drivers/net/sxe2/sxe2_ethdev.h
@@ -311,6 +311,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;
@@ -332,6 +337,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;
@@ -362,6 +368,8 @@ bool sxe2_ethdev_check(struct rte_eth_dev *dev);
 
 uint32_t sxe2_sched_mode_get(struct sxe2_adapter *adapter);
 
+int32_t sxe2_sched_reset(struct rte_eth_dev *dev);
+
 struct sxe2_pci_map_bar_info *sxe2_dev_get_bar_info(struct sxe2_adapter 
*adapter,
                                                    enum sxe2_pci_map_resource 
res_type);
 
diff --git a/drivers/net/sxe2/sxe2_irq.c b/drivers/net/sxe2/sxe2_irq.c
index c26098ef3a..1246cdbeef 100644
--- a/drivers/net/sxe2/sxe2_irq.c
+++ b/drivers/net/sxe2/sxe2_irq.c
@@ -47,6 +47,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 +93,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 79e65cfbf1..b5dd9950f0 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)
diff --git a/drivers/net/sxe2/sxe2_testpmd.c b/drivers/net/sxe2/sxe2_testpmd.c
new file mode 100644
index 0000000000..5792058212
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_testpmd.c
@@ -0,0 +1,733 @@
+
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef SXE2_TEST
+#include <cmdline_parse_num.h>
+#include <cmdline_parse_string.h>
+#include <stdlib.h>
+#include <testpmd.h>
+
+#include "sxe2_common_log.h"
+#include "sxe2_testpmd_lib.h"
+
+#define SXE2_SWITCH_BUFF_SIZE (4 * 1024 * 1024)
+
+struct cmd_stats_info_show_result {
+       cmdline_fixed_string_t sxe2;
+       cmdline_fixed_string_t show;
+       cmdline_fixed_string_t stats;
+       portid_t port_id;
+};
+cmdline_parse_token_string_t cmd_stats_info_sxe2 =
+       TOKEN_STRING_INITIALIZER(struct cmd_stats_info_show_result, sxe2, 
"sxe2");
+cmdline_parse_token_string_t cmd_stats_info_show =
+       TOKEN_STRING_INITIALIZER(struct cmd_stats_info_show_result, show, 
"show");
+cmdline_parse_token_string_t cmd_stats_info_stats =
+       TOKEN_STRING_INITIALIZER(struct cmd_stats_info_show_result, stats, 
"stats");
+cmdline_parse_token_num_t cmd_stats_info_port_id =
+       TOKEN_NUM_INITIALIZER(struct cmd_stats_info_show_result, port_id, 
RTE_UINT16);
+
+struct cmd_flow_rule_result {
+       cmdline_fixed_string_t sxe2;
+       cmdline_fixed_string_t flow;
+       cmdline_fixed_string_t rule;
+       cmdline_fixed_string_t dump;
+       portid_t port_id;
+};
+cmdline_parse_token_string_t cmd_flow_rule_sxe2 =
+       TOKEN_STRING_INITIALIZER(struct cmd_flow_rule_result, sxe2, "sxe2");
+cmdline_parse_token_string_t cmd_flow_rule_flow =
+       TOKEN_STRING_INITIALIZER(struct cmd_flow_rule_result, flow, "flow");
+cmdline_parse_token_string_t cmd_flow_rule_rule =
+       TOKEN_STRING_INITIALIZER(struct cmd_flow_rule_result, rule, "rule");
+cmdline_parse_token_string_t cmd_flow_rule_dmp =
+       TOKEN_STRING_INITIALIZER(struct cmd_flow_rule_result, dump, "dump");
+cmdline_parse_token_num_t cmd_flow_rule_port_id =
+       TOKEN_NUM_INITIALIZER(struct cmd_flow_rule_result, port_id, RTE_UINT16);
+
+struct cmd_udp_tunnel {
+       cmdline_fixed_string_t sxe2;
+       cmdline_fixed_string_t tunnel_type;
+       cmdline_fixed_string_t action;
+       cmdline_fixed_string_t udp_tunnel_port;
+       uint16_t               udp_port;
+       portid_t               port_id;
+};
+
+cmdline_parse_token_string_t cmd_udp_tunnel_sxe2 =
+       TOKEN_STRING_INITIALIZER(struct cmd_udp_tunnel, sxe2, "sxe2");
+cmdline_parse_token_string_t cmd_udp_tunnel_action =
+       TOKEN_STRING_INITIALIZER(struct cmd_udp_tunnel, action, "add#rm#show");
+cmdline_parse_token_string_t cmd_udp_tunnel_udp_tunnel_port =
+       TOKEN_STRING_INITIALIZER(struct cmd_udp_tunnel, udp_tunnel_port, 
"udp_tunnel_port");
+cmdline_parse_token_string_t cmd_udp_tunnel_tunnel_type =
+       TOKEN_STRING_INITIALIZER(struct cmd_udp_tunnel,
+       tunnel_type, 
"vxlan#vxlan-gpe#geneve#gtp-c#gtp-u#pfcp#ecpri#mpls#nvgre#l2tp#teredo");
+cmdline_parse_token_num_t cmd_udp_tunnel_udp_port =
+       TOKEN_NUM_INITIALIZER(struct cmd_udp_tunnel, udp_port, RTE_UINT16);
+cmdline_parse_token_num_t cmd_udp_tunnel_port_id  =
+       TOKEN_NUM_INITIALIZER(struct cmd_udp_tunnel, port_id, RTE_UINT16);
+
+struct cmd_sched_result {
+       cmdline_fixed_string_t sxe2;
+       cmdline_fixed_string_t sched;
+       cmdline_fixed_string_t reset;
+       portid_t port_id;
+};
+
+cmdline_parse_token_string_t cmd_sched_sxe2 =
+        TOKEN_STRING_INITIALIZER(struct cmd_sched_result, sxe2, "sxe2");
+cmdline_parse_token_string_t cmd_sched_sched =
+        TOKEN_STRING_INITIALIZER(struct cmd_sched_result, sched, "sched");
+cmdline_parse_token_string_t cmd_sched_reset =
+        TOKEN_STRING_INITIALIZER(struct cmd_sched_result, reset, "reset");
+cmdline_parse_token_num_t cmd_sched_port_id =
+        TOKEN_NUM_INITIALIZER(struct cmd_sched_result, port_id, RTE_UINT16);
+
+struct cmd_ipsec_result {
+       cmdline_fixed_string_t sxe2;
+       cmdline_fixed_string_t engin;
+       cmdline_fixed_string_t dir;
+       cmdline_fixed_string_t op;
+       portid_t port_id;
+       uint16_t session_id;
+       cmdline_fixed_string_t encrypt_algo;
+       cmdline_fixed_string_t encrypt_key;
+       cmdline_fixed_string_t auth_algo;
+       cmdline_fixed_string_t auth_key;
+       cmdline_fixed_string_t dst_ip;
+       uint16_t sport;
+       uint16_t dport;
+       uint32_t spi;
+};
+cmdline_parse_token_string_t cmd_ipsec_mgt_sxe2 =
+       TOKEN_STRING_INITIALIZER(struct cmd_ipsec_result, sxe2, "sxe2");
+cmdline_parse_token_string_t cmd_ipsec_mgt_module =
+       TOKEN_STRING_INITIALIZER(struct cmd_ipsec_result, engin, "ipsec");
+cmdline_parse_token_string_t cmd_ipsec_mgt_dir =
+       TOKEN_STRING_INITIALIZER(struct cmd_ipsec_result, dir, 
"egress#ingress");
+cmdline_parse_token_string_t cmd_ipsec_mgt_op =
+       TOKEN_STRING_INITIALIZER(struct cmd_ipsec_result, op, "add#rm#show");
+cmdline_parse_token_num_t cmd_ipsec_mgt_port_id =
+       TOKEN_NUM_INITIALIZER(struct cmd_ipsec_result, port_id, RTE_UINT16);
+cmdline_parse_token_num_t cmd_ipsec_mgt_session_id =
+       TOKEN_NUM_INITIALIZER(struct cmd_ipsec_result, session_id, RTE_UINT16);
+cmdline_parse_token_string_t cmd_ipsec_mgt_encrypt_algo =
+       TOKEN_STRING_INITIALIZER(struct cmd_ipsec_result, encrypt_algo, 
"aes-cbc#sm4-cbc#null");
+cmdline_parse_token_string_t cmd_ipsec_mgt_encrypt_key =
+       TOKEN_STRING_INITIALIZER(struct cmd_ipsec_result, encrypt_key, NULL);
+cmdline_parse_token_string_t cmd_ipsec_mgt_auth_algo =
+       TOKEN_STRING_INITIALIZER(struct cmd_ipsec_result, auth_algo, 
"sha-hmac#sm3-hmac#null");
+cmdline_parse_token_string_t cmd_ipsec_mgt_auth_key =
+       TOKEN_STRING_INITIALIZER(struct cmd_ipsec_result, auth_key, NULL);
+cmdline_parse_token_string_t cmd_ipsec_mgt_dst_ip =
+       TOKEN_STRING_INITIALIZER(struct cmd_ipsec_result, dst_ip, NULL);
+cmdline_parse_token_num_t cmd_ipsec_mgt_sport =
+       TOKEN_NUM_INITIALIZER(struct cmd_ipsec_result, sport, RTE_UINT16);
+cmdline_parse_token_num_t cmd_ipsec_mgt_dport =
+       TOKEN_NUM_INITIALIZER(struct cmd_ipsec_result, dport, RTE_UINT16);
+cmdline_parse_token_num_t cmd_ipsec_mgt_spi =
+       TOKEN_NUM_INITIALIZER(struct cmd_ipsec_result, spi, RTE_UINT32);
+
+struct cmd_ipsec_set_result {
+       cmdline_fixed_string_t sxe2;
+       cmdline_fixed_string_t engin;
+       cmdline_fixed_string_t op;
+       cmdline_fixed_string_t type;
+       portid_t port_id;
+       uint16_t conf_value;
+};
+cmdline_parse_token_string_t cmd_ipsec_set_sxe2 =
+       TOKEN_STRING_INITIALIZER(struct cmd_ipsec_set_result, sxe2, "sxe2");
+cmdline_parse_token_string_t cmd_ipsec_set_module =
+       TOKEN_STRING_INITIALIZER(struct cmd_ipsec_set_result, engin, "ipsec");
+cmdline_parse_token_string_t cmd_ipsec_set_op =
+       TOKEN_STRING_INITIALIZER(struct cmd_ipsec_set_result, op, "set#get");
+cmdline_parse_token_string_t cmd_ipsec_set_type =
+       TOKEN_STRING_INITIALIZER(struct cmd_ipsec_set_result, type, 
"session-id#esp-hdr-offset");
+cmdline_parse_token_num_t cmd_ipsec_set_port_id =
+       TOKEN_NUM_INITIALIZER(struct cmd_ipsec_set_result, port_id, RTE_UINT16);
+cmdline_parse_token_num_t cmd_ipsec_set_value =
+       TOKEN_NUM_INITIALIZER(struct cmd_ipsec_set_result, conf_value, 
RTE_UINT16);
+
+struct cmd_ipsec_flush_result {
+       cmdline_fixed_string_t sxe2;
+       cmdline_fixed_string_t engin;
+       cmdline_fixed_string_t op;
+       portid_t port_id;
+};
+cmdline_parse_token_string_t cmd_ipsec_flush_sxe2 =
+       TOKEN_STRING_INITIALIZER(struct cmd_ipsec_flush_result, sxe2, "sxe2");
+cmdline_parse_token_string_t cmd_ipsec_flush_module =
+       TOKEN_STRING_INITIALIZER(struct cmd_ipsec_flush_result, engin, "ipsec");
+cmdline_parse_token_string_t cmd_ipsec_flush_op =
+       TOKEN_STRING_INITIALIZER(struct cmd_ipsec_flush_result, op, "flush");
+cmdline_parse_token_num_t cmd_ipsec_flush_port_id =
+       TOKEN_NUM_INITIALIZER(struct cmd_ipsec_flush_result, port_id, 
RTE_UINT16);
+
+struct cmd_inject_irq {
+       cmdline_fixed_string_t sxe2;
+       cmdline_fixed_string_t inject;
+       cmdline_fixed_string_t irq;
+       portid_t port_id;
+       cmdline_fixed_string_t type;
+};
+cmdline_parse_token_string_t cmd_inject_irq_sxe2 =
+       TOKEN_STRING_INITIALIZER(struct cmd_inject_irq, sxe2, "sxe2");
+cmdline_parse_token_string_t cmd_inject_irq_inject =
+       TOKEN_STRING_INITIALIZER(struct cmd_inject_irq, inject, "inject");
+cmdline_parse_token_string_t cmd_inject_irq_irq =
+       TOKEN_STRING_INITIALIZER(struct cmd_inject_irq, irq, "irq");
+cmdline_parse_token_num_t cmd_inject_irq_port_id =
+       TOKEN_NUM_INITIALIZER(struct cmd_inject_irq, port_id, RTE_UINT16);
+cmdline_parse_token_string_t cmd_inject_irq_type =
+       TOKEN_STRING_INITIALIZER(struct cmd_inject_irq, type, "reset#lsc");
+
+static void cmd_dump_flow_rule_parsed(void *parsed_result,
+                                     struct cmdline *cl,
+                                     __rte_unused void *data)
+{
+       struct cmd_flow_rule_result *res = parsed_result;
+       int                          ret = -1;
+
+       ret = sxe2_flow_rule_dump(res->port_id, cl);
+       switch (ret) {
+       case 0:
+               break;
+       case -EINVAL:
+               cmdline_printf(cl, "Invalid parameters.\n");
+               break;
+       case -ENODEV:
+               cmdline_printf(cl, "Device doesn't support\n");
+               break;
+       default:
+               cmdline_printf(cl,
+                       "Failed to switch rule dump,"
+                       " error: (%s)\n",
+                       strerror(-ret));
+       }
+}
+
+static void cmd_udp_tunnel_set_parsed(void *parsed_result,
+                                     struct cmdline *cl,
+                                     __rte_unused void *data)
+{
+       struct cmd_udp_tunnel *res = parsed_result;
+       int32_t ret = -1;
+       uint8_t action;
+       const char *action_str[SXE2_TESTPMD_CMD_UDP_TUNNEL_MAX] = {
+               [SXE2_TESTPMD_CMD_UDP_TUNNEL_ADD] = "add",
+               [SXE2_TESTPMD_CMD_UDP_TUNNEL_DEL] = "rm",
+               [SXE2_TESTPMD_CMD_UDP_TUNNEL_GET] = "show"};
+
+       for (action = 0; action < SXE2_TESTPMD_CMD_UDP_TUNNEL_MAX; action++)
+               if (!strcmp(res->action, action_str[action]))
+                       break;
+
+       if (action >= SXE2_TESTPMD_CMD_UDP_TUNNEL_MAX) {
+               cmdline_printf(cl, "Invalid action!\n");
+               return;
+       }
+
+       ret = sxe2_udp_tunnel_operations(res->port_id, cl, action,
+                                        res->udp_port,
+                                        res->tunnel_type);
+       if (ret)
+               cmdline_printf(cl, "%s udp tunnel port failed, ret = %d\n",
+                               action_str[action], ret);
+}
+
+static void cmd_dump_stats_info_parsed(void *parsed_result,
+                                      struct cmdline *cl,
+                                      __rte_unused void *data)
+{
+       struct cmd_stats_info_show_result *res = parsed_result;
+       int ret = -1;
+
+       ret = sxe2_stats_info_show(res->port_id);
+       switch (ret) {
+       case 0:
+               break;
+       case -EINVAL:
+               cmdline_printf(cl, "Invalid parameters.\n");
+               break;
+       case -ENODEV:
+               cmdline_printf(cl, "Device doesn't support\n");
+               break;
+       default:
+               cmdline_printf(cl,
+                       "Failed to show stats info,"
+                       " error: (%s)\n", strerror(-ret));
+       }
+}
+
+static uint8_t cmd_ipsec_op_get(char *op)
+{
+       uint8_t i;
+       const char *op_type[SXE2_TESTPMD_CMD_IPSEC_OP_MAX] = {
+               [SXE2_TESTPMD_CMD_IPSEC_OP_ADD] = "add",
+               [SXE2_TESTPMD_CMD_IPSEC_OP_RM] = "rm",
+               [SXE2_TESTPMD_CMD_IPSEC_OP_SHOW] = "show",
+       };
+
+       for (i = 0; i < SXE2_TESTPMD_CMD_IPSEC_OP_MAX; i++) {
+               if (!strcmp(op, op_type[i]))
+                       break;
+       }
+
+       return i;
+}
+
+static uint8_t cmd_ipsec_dir_get(char *dir)
+{
+       uint8_t i;
+       const char *dir_type[SXE2_TESTPMD_CMD_IPSEC_DIR_MAX] = {
+               [SXE2_TESTPMD_CMD_IPSEC_DIR_EGRESS] = "egress",
+               [SXE2_TESTPMD_CMD_IPSEC_DIR_INGRESS] = "ingress"
+       };
+
+       for (i = 0; i < SXE2_TESTPMD_CMD_IPSEC_DIR_MAX; i++) {
+               if (!strcmp(dir, dir_type[i]))
+                       break;
+       }
+
+       return i;
+}
+
+static int sxe2_hex_to_val(char c)
+{
+       int val = 0;
+
+       if (c >= '0' && c <= '9')
+               val = c - '0';
+       if (c >= 'A' && c <= 'F')
+               val = 10 + c - 'A';
+       if (c >= 'a' && c <= 'f')
+               val = 10 + c - 'a';
+       return val;
+}
+
+static void sxe2_hex_to_bytes(uint8_t *enc_key, char *hex_str, uint8_t len)
+{
+       uint8_t i;
+       int high = 0;
+       int low = 0;
+
+       for (i = 0; i < len; i++) {
+               high = sxe2_hex_to_val(hex_str[2 * i]);
+               low = sxe2_hex_to_val(hex_str[2 * i + 1]);
+               enc_key[i] = (high << 4) | low;
+       }
+}
+
+static int32_t cmd_ipsec_add_param_fill(struct sxe2_ipsec_conf_param *param,
+                                       struct cmdline *cl,
+                                       struct cmd_ipsec_result *res)
+{
+       uint8_t i;
+       uint8_t j;
+       int32_t ret = -1;
+       const char *encrypt_algo[SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_MAX] = {
+               [SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_AES_CBC] = "aes-cbc",
+               [SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_SM4_CBC] = "sm4-cbc",
+               [SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_NULL] = "null"
+       };
+
+       const char *auth_algo[SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_MAX] = {
+               [SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_SHA_HMAC] = "sha-hmac",
+               [SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_SM3_HMAC] = "sm3-hmac",
+               [SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_NULL] = "null"
+       };
+
+       for (i = 0; i < SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_MAX; i++)
+               if (!strcmp(res->encrypt_algo, encrypt_algo[i]))
+                       break;
+
+       if (i >= SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_MAX) {
+               cmdline_printf(cl, "Invalid ipsec encrypt algo: %s!\n", 
res->encrypt_algo);
+               ret = -EINVAL;
+               goto l_end;
+       }
+
+       for (j = 0; j < SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_MAX; j++) {
+               if (!strcmp(res->auth_algo, auth_algo[j]))
+                       break;
+       }
+
+
+       if (j >= SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_MAX) {
+               cmdline_printf(cl, "Invalid ipsec auth algo: %s!\n", 
res->auth_algo);
+               ret = -EINVAL;
+               goto l_end;
+       }
+
+       param->encrypt_algo = i;
+       param->auth_algo = j;
+       if (param->encrypt_algo == SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_SM4_CBC)
+               param->enc_len = 16;
+       else
+               param->enc_len = 32;
+
+       sxe2_hex_to_bytes(param->enc_key, res->encrypt_key, param->enc_len);
+       if (param->auth_algo != SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_NULL) {
+               param->auth_len = 32;
+               sxe2_hex_to_bytes(param->auth_key, res->auth_key, 
param->auth_len);
+       }
+
+       ret = 0;
+
+l_end:
+       return ret;
+}
+
+static int32_t cmd_ipsec_egress_op_parsed(struct sxe2_ipsec_conf_param *param,
+                                         struct cmdline *cl,
+                                         struct cmd_ipsec_result *res)
+{
+       int32_t ret = -1;
+
+       switch (param->op) {
+       case SXE2_TESTPMD_CMD_IPSEC_OP_ADD:
+               ret = cmd_ipsec_add_param_fill(param, cl, res);
+               if (ret)
+                       goto l_end;
+               ret = sxe2_ipsec_egress_create(param, cl);
+               break;
+       case SXE2_TESTPMD_CMD_IPSEC_OP_RM:
+               param->session_id = res->session_id;
+               ret = sxe2_ipsec_egress_destroy(param, cl);
+               break;
+       case SXE2_TESTPMD_CMD_IPSEC_OP_SHOW:
+               ret = sxe2_ipsec_egress_show(param, cl);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+l_end:
+       return ret;
+}
+
+static int32_t cmd_ipsec_ip_addr_parsed(struct sxe2_ipsec_conf_param *param,
+                                       struct cmdline *cl,
+                                       struct cmd_ipsec_result *res)
+{
+       int32_t ret = -1;
+       struct in_addr addr4;
+       struct in6_addr addr6;
+
+       if (inet_pton(AF_INET, res->dst_ip, &addr4) == 1) {
+               param->ip_addr.type = RTE_SECURITY_IPSEC_TUNNEL_IPV4;
+               param->ip_addr.dst_ipv4 = addr4.s_addr;
+               ret = 0;
+       } else if (inet_pton(AF_INET6, res->dst_ip, &addr6) == 1) {
+               param->ip_addr.type = RTE_SECURITY_IPSEC_TUNNEL_IPV6;
+               memcpy(&param->ip_addr.dst_ipv6, &addr6, 
sizeof(param->ip_addr.dst_ipv6));
+               ret = 0;
+       } else {
+               cmdline_printf(cl, "Invalid ip address: %s!\n", res->dst_ip);
+               ret = -EINVAL;
+               goto l_end;
+       }
+
+l_end:
+       return ret;
+}
+
+static int32_t cmd_ipsec_ingress_op_parsed(struct sxe2_ipsec_conf_param *param,
+                                          struct cmdline *cl,
+                                          struct cmd_ipsec_result *res)
+{
+       int32_t ret = -1;
+
+       switch (param->op) {
+       case SXE2_TESTPMD_CMD_IPSEC_OP_ADD:
+               ret = cmd_ipsec_add_param_fill(param, cl, res);
+               if (ret)
+                       goto l_end;
+               param->sport = htons(res->sport);
+               param->dport = htons(res->dport);
+               param->spi = htonl(res->spi);
+               ret = cmd_ipsec_ip_addr_parsed(param, cl, res);
+               if (ret)
+                       goto l_end;
+               ret = sxe2_ipsec_ingress_create(param, cl);
+               break;
+       case SXE2_TESTPMD_CMD_IPSEC_OP_RM:
+               param->session_id = res->session_id;
+               ret = sxe2_ipsec_ingress_destroy(param, cl);
+               break;
+       case SXE2_TESTPMD_CMD_IPSEC_OP_SHOW:
+               ret = sxe2_ipsec_ingress_show(param, cl);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+l_end:
+       return ret;
+}
+
+static int32_t cmd_ipsec_dir_parsed(struct sxe2_ipsec_conf_param *param,
+                                   struct cmdline *cl,
+                                   struct cmd_ipsec_result *res)
+{
+       int32_t ret = -1;
+
+       switch (param->dir) {
+       case SXE2_TESTPMD_CMD_IPSEC_DIR_EGRESS:
+               ret = cmd_ipsec_egress_op_parsed(param, cl, res);
+               break;
+       case SXE2_TESTPMD_CMD_IPSEC_DIR_INGRESS:
+               ret = cmd_ipsec_ingress_op_parsed(param, cl, res);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static void cmd_ipsec_mgt_parsed(void *parsed_result,
+                                struct cmdline *cl,
+                                __rte_unused void *data)
+{
+       struct cmd_ipsec_result *res = parsed_result;
+       struct sxe2_ipsec_conf_param param;
+       int32_t ret = -1;
+       uint8_t dir = 0;
+       uint8_t op = 0;
+
+       dir = cmd_ipsec_dir_get(res->dir);
+       if (dir >= SXE2_TESTPMD_CMD_IPSEC_DIR_MAX) {
+               cmdline_printf(cl, "Invalid ipsec direction: %s!\n", res->dir);
+               ret = -EINVAL;
+               goto l_end;
+       }
+
+       op = cmd_ipsec_op_get(res->op);
+       if (op >= SXE2_TESTPMD_CMD_IPSEC_OP_MAX) {
+               cmdline_printf(cl, "Invalid ipsec operation: %s!\n", res->op);
+               ret = -EINVAL;
+               goto l_end;
+       }
+
+       memset(&param, 0, sizeof(struct sxe2_ipsec_conf_param));
+       param.dir = dir;
+       param.op = op;
+       param.port_id = res->port_id;
+       ret = cmd_ipsec_dir_parsed(&param, cl, res);
+
+       if (ret)
+               cmdline_printf(cl, "Command execute failed, ret = %d\n", ret);
+
+l_end:
+       return;
+}
+
+static void cmd_ipsec_set_parsed(void *parsed_result,
+                                struct cmdline *cl,
+                                __rte_unused void *data)
+{
+       struct cmd_ipsec_set_result *res = parsed_result;
+       int32_t ret = -1;
+
+       if (!strcmp(res->op, "set"))
+               ret = sxe2_ipsec_conf_set(res->port_id, cl, res->type, 
res->conf_value);
+       else if (!strcmp(res->op, "get"))
+               ret = sxe2_ipsec_conf_get(res->port_id, cl, res->type);
+       else
+               cmdline_printf(cl, "Invalid op: %s\n", res->op);
+
+       if (ret)
+               cmdline_printf(cl, "Command execute failed, ret = %d\n", ret);
+}
+
+static void cmd_ipsec_flush_parsed(void *parsed_result,
+                                  struct cmdline *cl,
+                                  __rte_unused void *data)
+{
+       struct cmd_ipsec_flush_result *res = parsed_result;
+       int32_t ret = -1;
+
+       ret = sxe2_ipsec_flush(res->port_id, cl);
+
+       if (ret)
+               cmdline_printf(cl, "Command execute failed, ret = %d\n", ret);
+}
+
+cmdline_parse_inst_t cmd_flow_rule_dump = {
+       .f        = cmd_dump_flow_rule_parsed,
+       .data     = NULL,
+       .help_str = "sxe2 flow rule dump <port_id>",
+       .tokens = {
+               (void *)&cmd_flow_rule_sxe2,
+               (void *)&cmd_flow_rule_flow,
+               (void *)&cmd_flow_rule_rule,
+               (void *)&cmd_flow_rule_dmp,
+               (void *)&cmd_flow_rule_port_id,
+               NULL,
+       },
+};
+
+cmdline_parse_inst_t cmd_udp_tunnel_set = {
+       .f        = cmd_udp_tunnel_set_parsed,
+       .data     = NULL,
+       .help_str = "sxe2 <port_id> udp_tunnel_port add|rm|show "
+                       
"vxlan|vxlan-gpe|geneve|gtp-c|gtp-u|pfcp|ecpri|mpls|nvgre|l2tp|teredo 
<udp_port>",
+       .tokens = {
+               (void *)&cmd_udp_tunnel_sxe2,
+               (void *)&cmd_udp_tunnel_port_id,
+               (void *)&cmd_udp_tunnel_udp_tunnel_port,
+               (void *)&cmd_udp_tunnel_action,
+               (void *)&cmd_udp_tunnel_tunnel_type,
+               (void *)&cmd_udp_tunnel_udp_port,
+               NULL,
+       },
+};
+
+cmdline_parse_inst_t cmd_stats_mgt = {
+       .f        = cmd_dump_stats_info_parsed,
+       .data     = NULL,
+       .help_str = "sxe2 show stats <port_id>",
+       .tokens = {
+               (void *)&cmd_stats_info_sxe2,
+               (void *)&cmd_stats_info_show,
+               (void *)&cmd_stats_info_stats,
+               (void *)&cmd_stats_info_port_id,
+               NULL,
+       },
+};
+
+static void cmd_sched_reset_cfg(void *parsed_result,
+                               struct cmdline *cl,
+                               __rte_unused void *data)
+{
+       struct cmd_sched_result *res = parsed_result;
+       int32_t ret = -1;
+
+       ret = sxe2_testpmd_sched_reset(res->port_id);
+       switch (ret) {
+       case 0:
+               break;
+       case -EINVAL:
+               cmdline_printf(cl, "invalid sched ops\n");
+               break;
+       case -ENOTSUP:
+               cmdline_printf(cl, "function not implemented\n");
+               break;
+       default:
+               cmdline_printf(cl, "programming error: (%s)\n",
+                       strerror(-ret));
+       }
+}
+
+cmdline_parse_inst_t cmd_sched_reset_cmd = {
+       .f        = cmd_sched_reset_cfg,
+       .data     = NULL,
+       .help_str = "sxe2 sched reset <port_id>",
+       .tokens = {
+               (void *)&cmd_sched_sxe2,
+               (void *)&cmd_sched_sched,
+               (void *)&cmd_sched_reset,
+               (void *)&cmd_sched_port_id,
+               NULL,
+       },
+};
+
+cmdline_parse_inst_t cmd_ipsec_mgt = {
+       .f = cmd_ipsec_mgt_parsed,
+       .data = NULL,
+       .help_str = "sxe2 ipsec egress|ingress add|rm|show "
+       "<port_id> <session_id> aes-cbc|sm4-cbc|null <encrypt_key> 
sha-hmac|sm3-hmac|null "
+       "<auth_key> <dst_ip> <sport> <dport> <spi>",
+       .tokens = {
+               (void *)&cmd_ipsec_mgt_sxe2,
+               (void *)&cmd_ipsec_mgt_module,
+               (void *)&cmd_ipsec_mgt_dir,
+               (void *)&cmd_ipsec_mgt_op,
+               (void *)&cmd_ipsec_mgt_port_id,
+               (void *)&cmd_ipsec_mgt_session_id,
+               (void *)&cmd_ipsec_mgt_encrypt_algo,
+               (void *)&cmd_ipsec_mgt_encrypt_key,
+               (void *)&cmd_ipsec_mgt_auth_algo,
+               (void *)&cmd_ipsec_mgt_auth_key,
+               (void *)&cmd_ipsec_mgt_dst_ip,
+               (void *)&cmd_ipsec_mgt_sport,
+               (void *)&cmd_ipsec_mgt_dport,
+               (void *)&cmd_ipsec_mgt_spi,
+               NULL,
+       },
+};
+
+cmdline_parse_inst_t cmd_ipsec_set = {
+       .f = cmd_ipsec_set_parsed,
+       .data = NULL,
+       .help_str = "sxe2 ipsec set|get esp-hdr-offset|session-id <port_id> 
<value>",
+       .tokens = {
+               (void *)&cmd_ipsec_set_sxe2,
+               (void *)&cmd_ipsec_set_module,
+               (void *)&cmd_ipsec_set_op,
+               (void *)&cmd_ipsec_set_type,
+               (void *)&cmd_ipsec_set_port_id,
+               (void *)&cmd_ipsec_set_value,
+               NULL,
+       },
+};
+
+cmdline_parse_inst_t cmd_ipsec_flush = {
+       .f = cmd_ipsec_flush_parsed,
+       .data = NULL,
+       .help_str = "sxe2 ipsec flush <port_id>.\n",
+       .tokens = {
+               (void *)&cmd_ipsec_flush_sxe2,
+               (void *)&cmd_ipsec_flush_module,
+               (void *)&cmd_ipsec_flush_op,
+               (void *)&cmd_ipsec_flush_port_id,
+               NULL,
+       },
+};
+
+static struct testpmd_driver_commands sxe2_cmds = {
+       .commands = {
+               {
+                       &cmd_udp_tunnel_set,
+                       "sxe2 udp tunnel port set.\n"
+                       "Add or remove a customed udp port for specific tunnel 
protocol\n\n",
+               },
+                       {
+                       &cmd_sched_reset_cmd,
+                       "sxe2 sched reset <port_id>.\n"
+                       "Reset sched node on the port\n\n",
+               },
+               {
+                       &cmd_stats_mgt,
+                       "sxe2 show stats.\n"
+                       "Dump a runtime sxe2 dev stats on a port\n\n",
+               },
+               {
+                       &cmd_ipsec_mgt,
+                       "sxe2 ipsec <dir> <op> <port_id> <session_id>  
<encrypt_algo> <encrypt_key>"
+                       "<encrypt_len> <auth_algo> <auth_key> <auth_len> 
<dst_ip> <sport> <dport> <spi>.\n"
+                       "Create/query/remove ipsec security session\n\n",
+               },
+               {
+                       &cmd_ipsec_set,
+                       "sxe2 ipsec set <port_id> <session_id> 
<esp_hdr_offset>.\n"
+                       "Set enabled tx session id or esp offset.\n\n",
+               },
+               {
+                       &cmd_ipsec_flush,
+                       "sxe2 ipsec flush <port_id>.\n"
+                       "Flush ipsec all configurations\n\n",
+               },
+               {       NULL, NULL},
+       },
+};
+TESTPMD_ADD_DRIVER_COMMANDS(sxe2_cmds)
+#endif
diff --git a/drivers/net/sxe2/sxe2_testpmd_lib.c 
b/drivers/net/sxe2/sxe2_testpmd_lib.c
new file mode 100644
index 0000000000..ab2530ffe6
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_testpmd_lib.c
@@ -0,0 +1,969 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#include <rte_bus.h>
+#include <eal_export.h>
+
+#include "sxe2_common_log.h"
+#include "sxe2_ethdev.h"
+#include "sxe2_stats.h"
+#include "sxe2_testpmd_lib.h"
+
+struct rte_mempool *g_sess_pool;
+
+bool g_sxe2_ipsec_mgt_init;
+struct sxe2_ipsec_session_mgt 
g_tx_session[SXE2_IPSEC_PORT_MAX][SXE2_IPSEC_SESSION_MAX];
+struct sxe2_ipsec_session_mgt 
g_rx_session[SXE2_IPSEC_PORT_MAX][SXE2_IPSEC_SESSION_MAX];
+uint16_t g_tx_sess_id[SXE2_IPSEC_PORT_MAX] = {0};
+uint16_t g_esp_header_offset[SXE2_IPSEC_PORT_MAX] = {0};
+
+static bool sxe2_is_supported(struct rte_eth_dev *dev)
+{
+       return sxe2_ethdev_check(dev);
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(sxe2_testpmd_sched_reset, 26.07)
+int32_t
+sxe2_testpmd_sched_reset(uint16_t port_id)
+{
+       struct rte_eth_dev   *dev     = NULL;
+
+       RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+
+       dev = &rte_eth_devices[port_id];
+       if (!sxe2_is_supported(dev)) {
+               PMD_LOG_ERR(DRV, "Invalid dev.");
+               return -ENODEV;
+       }
+
+       return sxe2_sched_reset(dev);
+}
+
+extern const char *sxe2_flow_type_name[SXE2_FLOW_TYPE_MAX];
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(sxe2_flow_rule_dump, 26.07)
+int32_t
+sxe2_flow_rule_dump(uint16_t port_id, struct cmdline *cl)
+{
+       struct rte_eth_dev            *dev           = NULL;
+       struct sxe2_adapter           *adapter       = NULL;
+       int32_t                            ret       = -1;
+       struct rte_flow_list_t        *flow_list     = NULL;
+       struct rte_flow               *flow          = NULL;
+       uint32_t                            index         = 0;
+       struct sxe2_flow              *hw_flow       = NULL;
+       uint8_t i = 0;
+
+       const char *sxe2_flow_engine_name[SXE2_FLOW_ENGINE_MAX] = {
+               [SXE2_FLOW_ENGINE_ACL] = "acl",
+               [SXE2_FLOW_ENGINE_RSS] = "rss",
+               [SXE2_FLOW_ENGINE_SWITCH] = "switch",
+               [SXE2_FLOW_ENGINE_FNAV] = "fnav",
+       };
+       const char *sxe2_flow_action_name[SXE2_FLOW_ACTION_MAX] = {
+               [SXE2_FLOW_ACTION_DROP] = "drop",
+               [SXE2_FLOW_ACTION_TC_REDIRECT] = "tc_redirect",
+               [SXE2_FLOW_ACTION_TO_VSI] = "to_vsi",
+               [SXE2_FLOW_ACTION_TO_VSI_LIST] = "to_vsi_list",
+               [SXE2_FLOW_ACTION_PASSTHRU] = "passthru",
+               [SXE2_FLOW_ACTION_QUEUE] = "queue",
+               [SXE2_FLOW_ACTION_Q_REGION] = "q_region",
+               [SXE2_FLOW_ACTION_MARK] = "mark",
+               [SXE2_FLOW_ACTION_COUNT] = "count",
+               [SXE2_FLOW_ACTION_RSS] = "rss",
+       };
+
+       RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+       dev = &rte_eth_devices[port_id];
+       if (!sxe2_is_supported(dev)) {
+               PMD_LOG_ERR(DRV, "Invalid dev");
+               ret = -ENODEV;
+               goto l_end;
+       }
+       adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+       flow_list = &adapter->flow_ctxt.rte_flow_list;
+       cmdline_printf(cl, "Dump sxe2 flow rule:\n");
+       TAILQ_FOREACH(flow, flow_list, next) {
+               cmdline_printf(cl, "rule index: %d\n", index++);
+               TAILQ_FOREACH(hw_flow, &flow->sxe2_flow_list, next) {
+                       cmdline_printf(cl, "\thw flow id: %d\n", 
hw_flow->flow_id);
+                       cmdline_printf(cl, "\t\ttype: %s\n",
+                                       
sxe2_flow_type_name[hw_flow->meta.flow_type]);
+                       cmdline_printf(cl, "\t\tprio: %d\n", 
hw_flow->meta.flow_prio);
+                       cmdline_printf(cl, "\t\tsrc vsi: %d,rule vsi: %d\n",
+                               hw_flow->meta.flow_src_vsi, 
hw_flow->meta.flow_rule_vsi);
+                       cmdline_printf(cl, "\t\tengine type: %s\n",
+                               sxe2_flow_engine_name[hw_flow->engine_type]);
+                       cmdline_printf(cl, "\t\taction:");
+                       for (i = 0; i < SXE2_FLOW_ACTION_MAX; i++) {
+                               if (sxe2_test_bit(i, hw_flow->action.act_types))
+                                       cmdline_printf(cl, "%s ", 
sxe2_flow_action_name[i]);
+                       }
+                       cmdline_printf(cl, "\n");
+               }
+       }
+       cmdline_printf(cl, "Dump sxe2 flow rule end.\n");
+       ret = 0;
+l_end:
+       return ret;
+}
+
+static const char *tunnel_type_list[SXE2_UDP_TUNNEL_MAX] = {
+       [SXE2_UDP_TUNNEL_PROTOCOL_VXLAN] = "vxlan",
+       [SXE2_UDP_TUNNEL_PROTOCOL_VXLAN_GPE] = "vxlan-gpe",
+       [SXE2_UDP_TUNNEL_PROTOCOL_GENEVE] = "geneve",
+       [SXE2_UDP_TUNNEL_PROTOCOL_GTP_C] = "gtp-c",
+       [SXE2_UDP_TUNNEL_PROTOCOL_GTP_U] = "gtp-u",
+       [SXE2_UDP_TUNNEL_PROTOCOL_PFCP] = "pfcp",
+       [SXE2_UDP_TUNNEL_PROTOCOL_ECPRI] = "ecpri",
+       [SXE2_UDP_TUNNEL_PROTOCOL_MPLS] = "mpls",
+       [SXE2_UDP_TUNNEL_PROTOCOL_NVGRE] = "nvgre",
+       [SXE2_UDP_TUNNEL_PROTOCOL_L2TP] = "l2tp",
+       [SXE2_UDP_TUNNEL_PROTOCOL_TEREDO] = "teredo"
+};
+
+static enum sxe2_udp_tunnel_protocol sxe2_udp_tunnel_type_str2proto(const char 
*tunnel_type)
+{
+       enum sxe2_udp_tunnel_protocol proto;
+
+       for (proto = 0; proto < SXE2_UDP_TUNNEL_MAX; proto++) {
+               if (tunnel_type_list[proto] != NULL &&
+                   strcmp(tunnel_type_list[proto], tunnel_type) == 0) {
+                       break;
+               }
+       }
+
+       return proto;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(sxe2_udp_tunnel_operations, 26.07)
+int32_t
+sxe2_udp_tunnel_operations(uint16_t port_id, struct cmdline *cl, uint8_t 
action,
+                          uint16_t udp_port, const char *tunnel_type)
+{
+       enum sxe2_udp_tunnel_protocol proto = 
sxe2_udp_tunnel_type_str2proto(tunnel_type);
+       struct rte_eth_dev            *dev = NULL;
+       struct sxe2_adapter           *adapter = NULL;
+       struct sxe2_udp_tunnel_cfg    tunnel_config = { 0 };
+       int32_t ret   = -1;
+
+       RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+       dev = &rte_eth_devices[port_id];
+       if (!sxe2_is_supported(dev)) {
+               PMD_LOG_ERR(DRV, "Invalid dev.");
+               ret = -ENODEV;
+               goto l_end;
+       }
+
+       if (proto >= SXE2_UDP_TUNNEL_MAX) {
+               cmdline_printf(cl, "Invalid tunnel type!\n");
+               goto l_end;
+       }
+       adapter = dev->data->dev_private;
+       switch (action) {
+       case SXE2_TESTPMD_CMD_UDP_TUNNEL_ADD:
+               ret = sxe2_udp_tunnel_port_add_common(adapter, proto, udp_port);
+               break;
+       case SXE2_TESTPMD_CMD_UDP_TUNNEL_DEL:
+               ret = sxe2_udp_tunnel_port_del_common(adapter, proto, udp_port);
+               break;
+       case SXE2_TESTPMD_CMD_UDP_TUNNEL_GET:
+               tunnel_config.protocol = proto;
+               ret = sxe2_udp_tunnel_port_get_common(adapter, &tunnel_config);
+               if (!ret) {
+                       cmdline_printf(cl, "Dump firmware udp tunnel config: 
[proto:%s, port:%d,"
+                               "enable:%d, src/dst:%d/%d, used:%d]\n",
+                                tunnel_type_list[proto], tunnel_config.fw_port,
+                                tunnel_config.fw_status, 
tunnel_config.fw_src_en,
+                                tunnel_config.fw_dst_en, 
tunnel_config.fw_used);
+               }
+               break;
+       default:
+       break;
+       }
+
+l_end:
+       return ret;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(sxe2_stats_info_show, 26.07)
+int32_t
+sxe2_stats_info_show(uint16_t port_id)
+{
+       struct rte_eth_dev *dev = NULL;
+
+       RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+
+       dev = &rte_eth_devices[port_id];
+       if (!sxe2_is_supported(dev)) {
+               PMD_LOG_ERR(DRV, "Invalid dev.");
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static int32_t sxe2_ipsec_init_mempools(void *sec_ctx)
+{
+       uint16_t nb_sess = 8192;
+       uint32_t sess_sz;
+       char s[64];
+       int32_t ret = -1;
+
+       sess_sz = rte_security_session_get_size(sec_ctx);
+       if (g_sess_pool == NULL) {
+               snprintf(s, sizeof(s), "sess_pool");
+               g_sess_pool = rte_mempool_create(s, nb_sess, sess_sz,
+                               MEMPOOL_CACHE_SIZE, 0,
+                               NULL, NULL, NULL, NULL,
+                               SOCKET_ID_ANY, 0);
+               if (g_sess_pool == NULL) {
+                       ret = -ENOMEM;
+                       PMD_LOG_ERR(DRV, "Failed to malloc session pool 
memory.");
+                       goto l_end;
+               }
+       }
+       ret = 0;
+
+l_end:
+       return ret;
+}
+
+static void sxe2_ipsec_init_session_mgt(void)
+{
+       uint16_t i;
+       uint8_t port_id;
+
+       if (g_sxe2_ipsec_mgt_init)
+               return;
+
+       for (port_id = 0; port_id < SXE2_IPSEC_PORT_MAX; port_id++) {
+               for (i = 0; i < SXE2_IPSEC_SESSION_MAX; i++) {
+                       g_tx_session[port_id][i].session = NULL;
+                       g_tx_session[port_id][i].encrypt_algo = 
SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_NULL;
+                       g_tx_session[port_id][i].auth_algo = 
SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_NULL;
+                       g_tx_session[port_id][i].session_id = i;
+                       g_tx_session[port_id][i].status = 0;
+               }
+       }
+
+       for (port_id = 0; port_id < SXE2_IPSEC_PORT_MAX; port_id++) {
+               for (i = 0; i < SXE2_IPSEC_SESSION_MAX; i++) {
+                       g_rx_session[port_id][i].session = NULL;
+                       g_rx_session[port_id][i].encrypt_algo = 
SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_NULL;
+                       g_rx_session[port_id][i].auth_algo = 
SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_NULL;
+                       g_rx_session[port_id][i].session_id = i;
+                       g_rx_session[port_id][i].status = 0;
+               }
+       }
+
+       g_sxe2_ipsec_mgt_init = true;
+}
+
+static uint16_t sxe2_ipsec_session_mgt_alloc(enum sxe2_testpmd_ipsec_dir dir, 
uint16_t port_id)
+{
+       uint16_t i;
+       uint16_t index = 0XFFFF;
+       struct sxe2_ipsec_session_mgt *mgt = NULL;
+
+       if (dir == SXE2_TESTPMD_CMD_IPSEC_DIR_EGRESS)
+               mgt = g_tx_session[port_id];
+       else
+               mgt = g_rx_session[port_id];
+
+       for (i = 0; i < SXE2_IPSEC_SESSION_MAX; i++) {
+               if (mgt[i].status == 0) {
+                       index = i;
+                       mgt[i].status = 1;
+                       break;
+               }
+       }
+
+       return index;
+}
+
+static void sxe2_ipsec_session_mgt_free(enum sxe2_testpmd_ipsec_dir dir,
+                                       uint16_t index, uint16_t port_id)
+{
+       struct sxe2_ipsec_session_mgt *mgt = NULL;
+
+       if (dir == SXE2_TESTPMD_CMD_IPSEC_DIR_EGRESS)
+               mgt = g_tx_session[port_id];
+       else
+               mgt = g_rx_session[port_id];
+
+       mgt[index].session = NULL;
+       mgt[index].status = 0;
+}
+
+static int32_t sxe2_ipsec_egress_construct(struct cmdline *cl,
+                                          struct rte_crypto_sym_xform **xform,
+                                          struct sxe2_ipsec_conf_param *param)
+{
+       struct rte_crypto_sym_xform *cur_xform = NULL;
+       struct rte_crypto_sym_xform *next_xform = NULL;
+       int32_t ret = -1;
+
+       cur_xform = rte_zmalloc("current xform",
+                               sizeof(struct rte_crypto_sym_xform), 0);
+       if (cur_xform == NULL) {
+               ret = -ENOMEM;
+               cmdline_printf(cl, "Failed to malloc memory!\n");
+               goto l_end;
+       }
+       cur_xform->type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+       cur_xform->cipher.op = RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+       if (param->encrypt_algo == SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_AES_CBC)
+               cur_xform->cipher.algo = SXE2_RTE_CRYPTO_CIPHER_AES_CBC;
+       else
+               cur_xform->cipher.algo = SXE2_RTE_RTE_CRYPTO_CIPHER_SM4_CBC;
+       cur_xform->cipher.key.length = param->enc_len;
+       cur_xform->cipher.key.data = param->enc_key;
+
+       if (param->auth_algo == SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_NULL) {
+               ret = 0;
+               goto l_end;
+       }
+
+       next_xform = rte_zmalloc("next xform",
+                               sizeof(struct rte_crypto_sym_xform), 0);
+       if (next_xform == NULL) {
+               rte_free(cur_xform);
+               ret = -ENOMEM;
+               cmdline_printf(cl, "Failed to malloc memory!\n");
+               goto l_end;
+       }
+       next_xform->type = RTE_CRYPTO_SYM_XFORM_AUTH;
+       next_xform->auth.op = RTE_CRYPTO_AUTH_OP_GENERATE;
+       if (param->auth_algo == SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_SHA_HMAC)
+               next_xform->auth.algo = SXE2_RTE_CRYPTO_AUTH_SHA256_HMAC;
+       else
+               next_xform->auth.algo = SXE2_RTE_CRYPTO_AUTH_SM3_HMAC;
+       next_xform->auth.key.length = param->auth_len;
+       next_xform->auth.key.data = param->auth_key;
+       cur_xform->next = next_xform;
+       ret = 0;
+
+l_end:
+       *xform = cur_xform;
+       return ret;
+}
+
+static int32_t sxe2_ipsec_ingress_construct(struct cmdline *cl,
+                                           struct rte_crypto_sym_xform **xform,
+                                           struct sxe2_ipsec_conf_param *param)
+{
+       struct rte_crypto_sym_xform *cur_xform = NULL;
+       struct rte_crypto_sym_xform *next_xform = NULL;
+       int32_t ret = -1;
+
+       cur_xform = rte_zmalloc("current xform",
+                               sizeof(struct rte_crypto_sym_xform), 0);
+       if (cur_xform == NULL) {
+               ret = -ENOMEM;
+               cmdline_printf(cl, "Failed to malloc memory!\n");
+               goto l_end;
+       }
+
+       if (param->auth_algo == SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_NULL) {
+               cur_xform->type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+               cur_xform->cipher.op = RTE_CRYPTO_CIPHER_OP_DECRYPT;
+               if (param->encrypt_algo == 
SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_AES_CBC)
+                       cur_xform->cipher.algo = SXE2_RTE_CRYPTO_CIPHER_AES_CBC;
+               else
+                       cur_xform->cipher.algo = 
SXE2_RTE_RTE_CRYPTO_CIPHER_SM4_CBC;
+               cur_xform->cipher.key.length = param->enc_len;
+               cur_xform->cipher.key.data = param->enc_key;
+               ret = 0;
+               goto l_end;
+       }
+
+       cur_xform->type = RTE_CRYPTO_SYM_XFORM_AUTH;
+       cur_xform->auth.op = RTE_CRYPTO_AUTH_OP_VERIFY;
+       if (param->auth_algo == SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_SHA_HMAC)
+               cur_xform->auth.algo = SXE2_RTE_CRYPTO_AUTH_SHA256_HMAC;
+       else
+               cur_xform->auth.algo = SXE2_RTE_CRYPTO_AUTH_SM3_HMAC;
+
+       cur_xform->auth.key.length = param->auth_len;
+       cur_xform->auth.key.data = param->auth_key;
+
+       next_xform = rte_zmalloc("next xform",
+                                sizeof(struct rte_crypto_sym_xform), 0);
+       if (next_xform == NULL) {
+               rte_free(cur_xform);
+               ret = -ENOMEM;
+               cmdline_printf(cl, "Failed to malloc memory!\n");
+               goto l_end;
+       }
+
+       next_xform->type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+       next_xform->cipher.op = RTE_CRYPTO_CIPHER_OP_DECRYPT;
+       if (param->encrypt_algo == SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_AES_CBC)
+               next_xform->cipher.algo = SXE2_RTE_CRYPTO_CIPHER_AES_CBC;
+       else
+               next_xform->cipher.algo = SXE2_RTE_RTE_CRYPTO_CIPHER_SM4_CBC;
+       next_xform->cipher.key.length = param->enc_len;
+       next_xform->cipher.key.data = param->enc_key;
+       cur_xform->next = next_xform;
+       ret = 0;
+
+l_end:
+       *xform = cur_xform;
+       return ret;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(sxe2_ipsec_ingress_create, 26.07)
+int32_t
+sxe2_ipsec_ingress_create(struct sxe2_ipsec_conf_param *param, struct cmdline 
*cl)
+{
+       struct rte_eth_dev *dev       = NULL;
+       struct rte_security_session_conf conf;
+       struct rte_crypto_sym_xform *encrypt_xform = NULL;
+       void *session = NULL;
+       struct rte_security_ctx *p_ctx = NULL;
+       int32_t ret = -1;
+       uint16_t index;
+       uint8_t i;
+
+       RTE_ETH_VALID_PORTID_OR_ERR_RET(param->port_id, -ENODEV);
+
+       dev = &rte_eth_devices[param->port_id];
+       if (!sxe2_is_supported(dev)) {
+               PMD_LOG_ERR(DRV, "Invalid dev.");
+               ret = -ENODEV;
+               goto l_end;
+       }
+
+       if (dev->data->dev_started != 0) {
+               cmdline_printf(cl, "port %d must be stopped.\n", 
dev->data->port_id);
+               ret = 0;
+               goto l_end;
+       }
+
+       p_ctx = rte_eth_dev_get_sec_ctx(param->port_id);
+
+       if (g_sess_pool == NULL) {
+               ret = sxe2_ipsec_init_mempools(p_ctx);
+               if (ret)
+                       goto l_end;
+       }
+
+       sxe2_ipsec_init_session_mgt();
+
+       memset(&conf, 0, sizeof(conf));
+       conf.protocol = RTE_SECURITY_PROTOCOL_IPSEC;
+       conf.action_type = RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO;
+       conf.ipsec.mode = RTE_SECURITY_IPSEC_SA_MODE_TUNNEL;
+       conf.ipsec.proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP;
+       conf.ipsec.direction = RTE_SECURITY_IPSEC_SA_DIR_INGRESS;
+       conf.ipsec.spi = param->spi;
+       conf.ipsec.udp.sport = param->sport;
+       conf.ipsec.udp.dport = param->dport;
+       conf.ipsec.tunnel.type = param->ip_addr.type;
+       if (param->sport || param->dport)
+               conf.ipsec.options.udp_encap = true;
+       if (param->ip_addr.type == RTE_SECURITY_IPSEC_TUNNEL_IPV4)
+               conf.ipsec.tunnel.ipv4.dst_ip.s_addr = param->ip_addr.dst_ipv4;
+       else
+               memcpy(&conf.ipsec.tunnel.ipv6.dst_addr,
+                      &param->ip_addr.dst_ipv6,
+                      sizeof(param->ip_addr.dst_ipv6));
+
+       ret = sxe2_ipsec_ingress_construct(cl, &encrypt_xform, param);
+       if (ret)
+               goto l_end;
+       conf.crypto_xform = encrypt_xform;
+
+       session = rte_security_session_create(p_ctx, &conf, g_sess_pool);
+       if (session == NULL) {
+               ret = -1;
+               goto l_free;
+       }
+
+       index = sxe2_ipsec_session_mgt_alloc(param->dir, param->port_id);
+       if (index == 0XFFFF) {
+               ret = -1;
+               goto l_free;
+       }
+
+       g_rx_session[param->port_id][index].session = session;
+       g_rx_session[param->port_id][index].encrypt_algo = param->encrypt_algo;
+       g_rx_session[param->port_id][index].auth_algo = param->auth_algo;
+       for (i = 0; i < 32; i++) {
+               g_rx_session[param->port_id][index].enc_key[i] = 
param->enc_key[i];
+               g_rx_session[param->port_id][index].auth_key[i] = 
param->auth_key[i];
+       }
+       g_rx_session[param->port_id][index].sport = ntohs(param->sport);
+       g_rx_session[param->port_id][index].dport = ntohs(param->dport);
+       g_rx_session[param->port_id][index].spi = ntohl(param->spi);
+       memcpy(&g_rx_session[param->port_id][index].ip_addr,
+              &param->ip_addr,
+              sizeof(struct sxe2_ipsec_ip_param));
+
+       ret = 0;
+
+l_free:
+       if (encrypt_xform->next)
+               rte_free(encrypt_xform->next);
+       if (encrypt_xform)
+               rte_free(encrypt_xform);
+
+l_end:
+       return ret;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(sxe2_ipsec_ingress_destroy, 26.07)
+int32_t
+sxe2_ipsec_ingress_destroy(struct sxe2_ipsec_conf_param *param, struct cmdline 
*cl)
+{
+       struct rte_eth_dev *dev       = NULL;
+       struct rte_security_ctx *p_ctx = NULL;
+       struct rte_security_session *session = NULL;
+       int32_t ret = -1;
+
+       RTE_ETH_VALID_PORTID_OR_ERR_RET(param->port_id, -ENODEV);
+
+       dev = &rte_eth_devices[param->port_id];
+       if (!sxe2_is_supported(dev)) {
+               cmdline_printf(cl, "Invalid dev.\n");
+               ret = -ENODEV;
+               goto l_end;
+       }
+
+       if (dev->data->dev_started != 0) {
+               cmdline_printf(cl, "port %d must be stopped.\n", 
dev->data->port_id);
+               ret = 0;
+               goto l_end;
+       }
+
+       if (param->session_id >= SXE2_IPSEC_SESSION_MAX) {
+               PMD_LOG_ERR(DRV, "Invalid session id.");
+               ret = -EINVAL;
+               goto l_end;
+       }
+
+       if (!g_rx_session[param->port_id][param->session_id].status) {
+               PMD_LOG_ERR(DRV, "Invalid session status.");
+               ret = -EINVAL;
+               goto l_end;
+       }
+
+       if (g_rx_session[param->port_id][param->session_id].session == NULL) {
+               PMD_LOG_ERR(DRV, "Invalid session data.");
+               ret = -EINVAL;
+               goto l_end;
+       }
+
+       p_ctx = rte_eth_dev_get_sec_ctx(param->port_id);
+
+       session = g_rx_session[param->port_id][param->session_id].session;
+       ret = rte_security_session_destroy(p_ctx, session);
+       if (ret)
+               goto l_end;
+       sxe2_ipsec_session_mgt_free(param->dir, param->session_id, 
param->port_id);
+
+       ret = 0;
+l_end:
+       return ret;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(sxe2_ipsec_ingress_show, 26.07)
+int32_t
+sxe2_ipsec_ingress_show(struct sxe2_ipsec_conf_param *param, struct cmdline 
*cl)
+{
+       struct rte_eth_dev *dev       = NULL;
+       int32_t ret = -1;
+       uint16_t i;
+       uint8_t j;
+       char encrypt_key[65];
+       char auth_key[65];
+       const char *encrypt_algo[SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_MAX] = {
+               [SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_AES_CBC] = "aes-cbc",
+               [SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_SM4_CBC] = "sm4-cbc",
+               [SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_NULL] = "null"
+       };
+
+       const char *auth_algo[SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_MAX] = {
+               [SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_SHA_HMAC] = "sha-hmac",
+               [SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_SM3_HMAC] = "sm3-hmac",
+               [SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_NULL] = "null"
+       };
+
+       RTE_ETH_VALID_PORTID_OR_ERR_RET(param->port_id, -ENODEV);
+
+       dev = &rte_eth_devices[param->port_id];
+       if (!sxe2_is_supported(dev)) {
+               PMD_LOG_ERR(DRV, "Invalid dev.");
+               ret = -ENODEV;
+               goto l_end;
+       }
+
+       for (i = 0; i < SXE2_IPSEC_SESSION_MAX; i++) {
+               if (g_rx_session[param->port_id][i].status &&
+                   g_rx_session[param->port_id][i].session) {
+                       memset(encrypt_key, '\0', sizeof(encrypt_key));
+                       memset(auth_key, '\0', sizeof(auth_key));
+                       for (j = 0; j < 32; j++) {
+                               sprintf(encrypt_key + 2 * j, "%02x",
+                                       
g_rx_session[param->port_id][i].enc_key[j]);
+                       }
+
+                       if (g_rx_session[param->port_id][i].auth_algo !=
+                           SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_NULL) {
+                               for (j = 0; j < 32; j++) {
+                                       sprintf(auth_key + 2 * j, "%02x",
+                                               
g_rx_session[param->port_id][i].auth_key[j]);
+                               }
+                       }
+
+                       cmdline_printf(cl, "session_id:%u, direction:rx ,"
+                               "encrypt_algo:%s, encrypt_key:0x%s,"
+                               "auth_algo:%s, auth_key:0x%s, sport:%u, 
dport:%u, spi:%u\n",
+                               i,
+                               
encrypt_algo[g_rx_session[param->port_id][i].encrypt_algo],
+                               encrypt_key,
+                               
auth_algo[g_rx_session[param->port_id][i].auth_algo],
+                               auth_key,
+                               g_rx_session[param->port_id][i].sport,
+                               g_rx_session[param->port_id][i].dport,
+                               g_rx_session[param->port_id][i].spi);
+               }
+       }
+
+       ret = 0;
+
+l_end:
+       return ret;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(sxe2_ipsec_egress_create, 26.07)
+int32_t
+sxe2_ipsec_egress_create(struct sxe2_ipsec_conf_param *param, struct cmdline 
*cl)
+{
+       struct rte_eth_dev *dev       = NULL;
+       struct rte_security_session_conf conf;
+       struct rte_crypto_sym_xform *encrypt_xform = NULL;
+       void *session = NULL;
+       struct rte_security_ctx *p_ctx = NULL;
+       int32_t ret = -1;
+       uint16_t index;
+       uint8_t i;
+
+       RTE_ETH_VALID_PORTID_OR_ERR_RET(param->port_id, -ENODEV);
+
+       dev = &rte_eth_devices[param->port_id];
+       if (!sxe2_is_supported(dev)) {
+               PMD_LOG_ERR(DRV, "Invalid dev.");
+               ret = -ENODEV;
+               goto l_end;
+       }
+
+       if (dev->data->dev_started != 0) {
+               cmdline_printf(cl, "port %d must be stopped.\n", 
dev->data->port_id);
+               ret = 0;
+               goto l_end;
+       }
+
+       p_ctx = rte_eth_dev_get_sec_ctx(param->port_id);
+
+       if (g_sess_pool == NULL) {
+               ret = sxe2_ipsec_init_mempools(p_ctx);
+               if (ret)
+                       goto l_end;
+       }
+
+       sxe2_ipsec_init_session_mgt();
+
+       memset(&conf, 0, sizeof(conf));
+       conf.protocol = RTE_SECURITY_PROTOCOL_IPSEC;
+       conf.action_type = RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO;
+       conf.ipsec.mode = RTE_SECURITY_IPSEC_SA_MODE_TUNNEL;
+       conf.ipsec.proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP;
+       conf.ipsec.direction = RTE_SECURITY_IPSEC_SA_DIR_EGRESS;
+
+       ret = sxe2_ipsec_egress_construct(cl, &encrypt_xform, param);
+       if (ret)
+               goto l_end;
+       conf.crypto_xform = encrypt_xform;
+
+       session = rte_security_session_create(p_ctx, &conf, g_sess_pool);
+       if (session == NULL) {
+               ret = -1;
+               goto l_free;
+       }
+
+       index = sxe2_ipsec_session_mgt_alloc(param->dir, param->port_id);
+       if (index == 0XFFFF) {
+               ret = -1;
+               goto l_free;
+       }
+
+       g_tx_session[param->port_id][index].session = session;
+       g_tx_session[param->port_id][index].encrypt_algo = param->encrypt_algo;
+       g_tx_session[param->port_id][index].auth_algo = param->auth_algo;
+       for (i = 0; i < 32; i++) {
+               g_tx_session[param->port_id][index].enc_key[i] = 
param->enc_key[i];
+               g_tx_session[param->port_id][index].auth_key[i] = 
param->auth_key[i];
+       }
+       ret = 0;
+
+l_free:
+       if (encrypt_xform->next)
+               rte_free(encrypt_xform->next);
+       if (encrypt_xform)
+               rte_free(encrypt_xform);
+
+l_end:
+       return ret;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(sxe2_ipsec_egress_destroy, 26.07)
+int32_t
+sxe2_ipsec_egress_destroy(struct sxe2_ipsec_conf_param *param, struct cmdline 
*cl)
+{
+       struct rte_eth_dev *dev       = NULL;
+       struct rte_security_ctx *p_ctx = NULL;
+       struct rte_security_session *session = NULL;
+       int32_t ret = -1;
+
+       RTE_ETH_VALID_PORTID_OR_ERR_RET(param->port_id, -ENODEV);
+
+       dev = &rte_eth_devices[param->port_id];
+       if (!sxe2_is_supported(dev)) {
+               PMD_LOG_ERR(DRV, "Invalid dev.");
+               ret = -ENODEV;
+               goto l_end;
+       }
+
+       if (dev->data->dev_started != 0) {
+               cmdline_printf(cl, "port %d must be stopped.\n", 
dev->data->port_id);
+               ret = 0;
+               goto l_end;
+       }
+
+       if (param->session_id >= SXE2_IPSEC_SESSION_MAX) {
+               PMD_LOG_ERR(DRV, "Invalid session id.");
+               ret = -EINVAL;
+               goto l_end;
+       }
+
+       if (!g_tx_session[param->port_id][param->session_id].status) {
+               PMD_LOG_ERR(DRV, "Invalid session status.");
+               ret = -EINVAL;
+               goto l_end;
+       }
+
+       if (g_tx_session[param->port_id][param->session_id].session == NULL) {
+               PMD_LOG_ERR(DRV, "Invalid session data.");
+               ret = -EINVAL;
+               goto l_end;
+       }
+
+       p_ctx = rte_eth_dev_get_sec_ctx(param->port_id);
+
+       session = g_tx_session[param->port_id][param->session_id].session;
+       ret = rte_security_session_destroy(p_ctx, session);
+       if (ret)
+               goto l_end;
+       sxe2_ipsec_session_mgt_free(param->dir, param->session_id, 
param->port_id);
+
+       ret = 0;
+
+l_end:
+       return ret;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(sxe2_ipsec_egress_show, 26.07)
+int32_t
+sxe2_ipsec_egress_show(struct sxe2_ipsec_conf_param *param, struct cmdline *cl)
+{
+       struct rte_eth_dev *dev       = NULL;
+       int32_t ret = -1;
+       uint16_t i;
+       uint8_t j;
+       char encrypt_key[65];
+       char auth_key[65];
+       const char *encrypt_algo[SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_MAX] = {
+               [SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_AES_CBC] = "aes-cbc",
+               [SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_SM4_CBC] = "sm4-cbc",
+               [SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_NULL] = "null"
+       };
+
+       const char *auth_algo[SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_MAX] = {
+               [SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_SHA_HMAC] = "sha-hmac",
+               [SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_SM3_HMAC] = "sm3-hmac",
+               [SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_NULL] = "null"
+       };
+
+       RTE_ETH_VALID_PORTID_OR_ERR_RET(param->port_id, -ENODEV);
+
+       dev = &rte_eth_devices[param->port_id];
+       if (!sxe2_is_supported(dev)) {
+               PMD_LOG_ERR(DRV, "Invalid dev.");
+               ret = -ENODEV;
+               goto l_end;
+       }
+
+       for (i = 0; i < SXE2_IPSEC_SESSION_MAX; i++) {
+               if (g_tx_session[param->port_id][i].status &&
+                   g_tx_session[param->port_id][i].session) {
+                       memset(encrypt_key, '\0', sizeof(encrypt_key));
+                       memset(auth_key, '\0', sizeof(auth_key));
+                       for (j = 0; j < 32; j++)
+                               sprintf(encrypt_key + 2 * j, "%02x",
+                                       
g_tx_session[param->port_id][i].enc_key[j]);
+                       if (g_tx_session[param->port_id][i].auth_algo !=
+                           SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_NULL)
+                               for (j = 0; j < 32; j++)
+                                       sprintf(auth_key + 2 * j, "%02x",
+                                               
g_tx_session[param->port_id][i].auth_key[j]);
+
+                       cmdline_printf(cl, "id:%u, tx , encrypt_algo:%s,"
+                               "encrypt_key:0x%s, auth_algo:%s, 
auth_key:0x%s.\n",
+                               i,
+                               
encrypt_algo[g_tx_session[param->port_id][i].encrypt_algo],
+                               encrypt_key,
+                               
auth_algo[g_tx_session[param->port_id][i].auth_algo],
+                               auth_key);
+               }
+       }
+
+       ret = 0;
+
+l_end:
+       return ret;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(sxe2_ipsec_conf_get, 26.07)
+int32_t
+sxe2_ipsec_conf_get(uint16_t port_id, struct cmdline *cl, char type[])
+{
+       struct rte_eth_dev *dev = NULL;
+
+       RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+
+       dev = &rte_eth_devices[port_id];
+       if (!sxe2_is_supported(dev)) {
+               PMD_LOG_ERR(DRV, "Invalid dev.");
+               return -ENODEV;
+       }
+       if (!strcmp(type, "session-id"))
+               cmdline_printf(cl, "session-id: %u\n",
+                       g_tx_sess_id[port_id]);
+       else if (!strcmp(type, "esp-hdr-offset"))
+               cmdline_printf(cl, "esp-hdr-offset: %u\n",
+                       g_esp_header_offset[port_id]);
+       else
+               cmdline_printf(cl, "Invalid type: %s\n", type);
+
+       return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(sxe2_ipsec_conf_set, 26.07)
+int32_t
+sxe2_ipsec_conf_set(uint16_t port_id, struct cmdline *cl, char type[], 
uint16_t value)
+{
+       struct rte_eth_dev *dev = NULL;
+
+       RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+
+       dev = &rte_eth_devices[port_id];
+       if (!sxe2_is_supported(dev)) {
+               PMD_LOG_ERR(DRV, "Invalid dev.");
+               return -ENODEV;
+       }
+       if (!strcmp(type, "session-id")) {
+               if (value >= 4096 || !g_tx_session[port_id][value].status) {
+                       cmdline_printf(cl, "Invalid session-id: %u,"
+                               "0 <= value <= 4095 or the session is 
inactive.\n", value);
+                       return -EINVAL;
+               }
+               g_tx_sess_id[port_id] = value;
+               cmdline_printf(cl, "session-id: %u\n", g_tx_sess_id[port_id]);
+       } else if (!strcmp(type, "esp-hdr-offset")) {
+               if (value < 34 || value > 512) {
+                       cmdline_printf(cl, "Invalid esp-hdr-offset: %u,"
+                                      "34 <= value <= 512.\n", value);
+                       return -EINVAL;
+               }
+               g_esp_header_offset[port_id] = value;
+               cmdline_printf(cl, "esp-hdr-offset: %u\n",
+                       g_esp_header_offset[port_id]);
+       } else {
+               cmdline_printf(cl, "Invalid type: %s\n", type);
+       }
+
+       return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(sxe2_ipsec_stats_show, 26.07)
+int32_t
+sxe2_ipsec_stats_show(uint16_t port_id)
+{
+       (void)port_id;
+       return 0;
+}
+
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(sxe2_ipsec_flush, 26.07)
+int32_t
+sxe2_ipsec_flush(uint16_t port_id, struct cmdline *cl)
+{
+       struct rte_eth_dev   *dev     = NULL;
+       struct rte_security_ctx *p_ctx = NULL;
+       struct rte_security_session *session = NULL;
+       int32_t ret = -1;
+       uint16_t i;
+
+       RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+
+       dev = &rte_eth_devices[port_id];
+       if (!sxe2_is_supported(dev)) {
+               cmdline_printf(cl, "Invalid dev.\n");
+               ret = -ENODEV;
+               goto l_end;
+       }
+
+       if (dev->data->dev_started != 0) {
+               cmdline_printf(cl, "port %d must be stopped.\n", 
dev->data->port_id);
+               ret = 0;
+               goto l_end;
+       }
+
+       p_ctx = rte_eth_dev_get_sec_ctx(port_id);
+
+       g_esp_header_offset[port_id] = 0;
+       g_tx_sess_id[port_id] = 0;
+
+       for (i = 0; i < SXE2_IPSEC_SESSION_MAX; i++) {
+               session = g_tx_session[port_id][i].session;
+               if (g_tx_session[port_id][i].status && session) {
+                       ret = rte_security_session_destroy(p_ctx, session);
+                       if (ret)
+                               cmdline_printf(cl, "failed to destroy tx 
session: %d.\n", i);
+                       else
+                               
sxe2_ipsec_session_mgt_free(SXE2_TESTPMD_CMD_IPSEC_DIR_EGRESS,
+                                                           i, port_id);
+               }
+       }
+
+       for (i = 0; i < SXE2_IPSEC_SESSION_MAX; i++) {
+               session = g_rx_session[port_id][i].session;
+               if (g_rx_session[port_id][i].status && session) {
+                       ret = rte_security_session_destroy(p_ctx, session);
+                       if (ret)
+                               cmdline_printf(cl, "failed to destroy rx 
session: %d.\n", i);
+                       else
+                               
sxe2_ipsec_session_mgt_free(SXE2_TESTPMD_CMD_IPSEC_DIR_INGRESS,
+                                                           i, port_id);
+               }
+       }
+
+       g_sxe2_ipsec_mgt_init = false;
+       ret = 0;
+
+l_end:
+       return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_testpmd_lib.h 
b/drivers/net/sxe2/sxe2_testpmd_lib.h
new file mode 100644
index 0000000000..3d2659ef00
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_testpmd_lib.h
@@ -0,0 +1,142 @@
+
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_TESTPMD_LIB_H__
+#define __SXE2_TESTPMD_LIB_H__
+#include <cmdline.h>
+#include "sxe2_ipsec.h"
+
+#define SXE2_IPSEC_SESSION_MAX (4096)
+#define SXE2_IPSEC_PORT_MAX  RTE_MAX_ETHPORTS
+#define MEMPOOL_CACHE_SIZE (512 / 2)
+
+enum {
+       SXE2_TESTPMD_CMD_UDP_TUNNEL_ADD = 0,
+       SXE2_TESTPMD_CMD_UDP_TUNNEL_DEL = 1,
+       SXE2_TESTPMD_CMD_UDP_TUNNEL_GET = 2,
+       SXE2_TESTPMD_CMD_UDP_TUNNEL_MAX,
+};
+
+enum sxe2_testpmd_ipsec_op {
+       SXE2_TESTPMD_CMD_IPSEC_OP_ADD = 0,
+       SXE2_TESTPMD_CMD_IPSEC_OP_RM = 1,
+       SXE2_TESTPMD_CMD_IPSEC_OP_SHOW = 2,
+       SXE2_TESTPMD_CMD_IPSEC_OP_MAX,
+};
+
+enum sxe2_testpmd_ipsec_dir {
+       SXE2_TESTPMD_CMD_IPSEC_DIR_EGRESS = 0,
+       SXE2_TESTPMD_CMD_IPSEC_DIR_INGRESS = 1,
+       SXE2_TESTPMD_CMD_IPSEC_DIR_MAX,
+};
+
+enum sxe2_testpmd_ipsec_encrypt_algo {
+       SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_AES_CBC = 0,
+       SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_SM4_CBC = 1,
+       SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_NULL = 2,
+       SXE2_TESTPMD_CMD_IPSEC_EN_ALGO_MAX,
+};
+
+enum sxe2_testpmd_ipsec_auth_algo {
+       SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_SHA_HMAC = 0,
+       SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_SM3_HMAC = 1,
+       SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_NULL = 2,
+       SXE2_TESTPMD_CMD_IPSEC_AUTH_ALGO_MAX,
+};
+
+struct sxe2_ipsec_conf_param {
+       enum sxe2_testpmd_ipsec_dir dir;
+       enum sxe2_testpmd_ipsec_op op;
+       enum sxe2_testpmd_ipsec_encrypt_algo encrypt_algo;
+       enum sxe2_testpmd_ipsec_auth_algo auth_algo;
+       struct sxe2_ipsec_ip_param ip_addr;
+       uint32_t spi;
+       uint16_t port_id;
+       uint16_t session_id;
+       uint16_t sport;
+       uint16_t dport;
+       uint8_t enc_key[32];
+       uint8_t enc_len;
+       uint8_t auth_key[32];
+       uint8_t auth_len;
+};
+
+struct sxe2_ipsec_session_mgt {
+       void *session;
+       enum sxe2_testpmd_ipsec_encrypt_algo encrypt_algo;
+       enum sxe2_testpmd_ipsec_auth_algo auth_algo;
+       struct sxe2_ipsec_ip_param ip_addr;
+       uint32_t spi;
+       uint16_t session_id;
+       uint16_t sport;
+       uint16_t dport;
+       uint8_t enc_key[32];
+       uint8_t auth_key[32];
+       uint8_t status;
+};
+
+__rte_experimental
+int32_t
+sxe2_testpmd_sched_reset(uint16_t port_id);
+
+__rte_experimental
+int32_t
+sxe2_flow_rule_dump(uint16_t port_id, struct cmdline *cl);
+
+__rte_experimental
+int32_t
+sxe2_udp_tunnel_operations(uint16_t port_id, struct cmdline *cl, uint8_t 
action,
+                          uint16_t udp_port, const char *tunnel_type);
+
+__rte_experimental
+int32_t
+sxe2_stats_info_show(uint16_t port_id);
+
+__rte_experimental
+int32_t
+sxe2_ipsec_ingress_create(struct sxe2_ipsec_conf_param *param, struct cmdline 
*cl);
+
+__rte_experimental
+int32_t
+sxe2_ipsec_ingress_destroy(struct sxe2_ipsec_conf_param *param, struct cmdline 
*cl);
+
+__rte_experimental
+int32_t
+sxe2_ipsec_ingress_show(struct sxe2_ipsec_conf_param *param, struct cmdline 
*cl);
+
+__rte_experimental
+int32_t
+sxe2_ipsec_egress_create(struct sxe2_ipsec_conf_param *param, struct cmdline 
*cl);
+
+__rte_experimental
+int32_t
+sxe2_ipsec_egress_destroy(struct sxe2_ipsec_conf_param *param, struct cmdline 
*cl);
+
+__rte_experimental
+int32_t
+sxe2_ipsec_egress_show(struct sxe2_ipsec_conf_param *param, struct cmdline 
*cl);
+
+__rte_experimental
+int32_t
+sxe2_ipsec_conf_get(uint16_t port_id, struct cmdline *cl, char type[]);
+
+__rte_experimental
+int32_t
+sxe2_ipsec_conf_set(uint16_t port_id, struct cmdline *cl, char type[], 
uint16_t value);
+
+__rte_experimental
+int32_t
+sxe2_ipsec_stats_show(uint16_t port_id);
+
+__rte_experimental
+int32_t
+sxe2_ipsec_flush(uint16_t port_id, struct cmdline *cl);
+
+extern struct sxe2_ipsec_session_mgt 
g_tx_session[SXE2_IPSEC_PORT_MAX][SXE2_IPSEC_SESSION_MAX];
+extern uint16_t g_tx_sess_id[SXE2_IPSEC_PORT_MAX];
+extern uint16_t g_esp_header_offset[SXE2_IPSEC_PORT_MAX];
+extern struct rte_mempool *g_sess_pool;
+
+#endif /* __SXE2_TESTPMD_LIB_H__ */
diff --git a/drivers/net/sxe2/sxe2_tm.c b/drivers/net/sxe2/sxe2_tm.c
index 4c4f793cd5..5de9b5d3b7 100644
--- a/drivers/net/sxe2/sxe2_tm.c
+++ b/drivers/net/sxe2/sxe2_tm.c
@@ -982,6 +982,24 @@ int32_t sxe2_tm_init(struct rte_eth_dev *dev)
        return ret;
 }
 
+int32_t sxe2_tm_conf_reset(struct rte_eth_dev *dev)
+{
+       int32_t ret;
+
+       ret = sxe2_tm_uninit(dev);
+       if (ret)
+               goto l_end;
+
+       ret = sxe2_tm_init(dev);
+       if (ret)
+               goto l_end;
+
+       PMD_LOG_DEBUG(DRV, "Tm config reset succeed.");
+
+l_end:
+       return ret;
+}
+
 static int32_t sxe2_tm_chk_all_leaf(struct rte_eth_dev *dev)
 {
        int32_t ret = 0;
diff --git a/drivers/net/sxe2/sxe2_tm.h b/drivers/net/sxe2/sxe2_tm.h
index c4f8da6a8e..b0bfc2091d 100644
--- a/drivers/net/sxe2/sxe2_tm.h
+++ b/drivers/net/sxe2/sxe2_tm.h
@@ -73,4 +73,6 @@ int32_t sxe2_tm_init(struct rte_eth_dev *dev);
 
 int32_t sxe2_tm_uninit(struct rte_eth_dev *dev);
 
+int32_t sxe2_tm_conf_reset(struct rte_eth_dev *dev);
+
 #endif /* __SXE2_TM_H__ */
-- 
2.52.0

Reply via email to