From: Renyong Wan <wa...@3snic.com>

Signed-off-by: Steven Song <steven.s...@3snic.com>
Signed-off-by: Renyong Wan <wa...@3snic.com>
---
v2:
* Fixed 'mask->hdr.src_addr' will always evaluate to 'true'.
* Removed error.h from including files.
---
 doc/guides/nics/features/sssnic.ini     |   12 +
 drivers/net/sssnic/base/sssnic_api.c    |  264 ++++++
 drivers/net/sssnic/base/sssnic_api.h    |   22 +
 drivers/net/sssnic/base/sssnic_cmd.h    |   71 ++
 drivers/net/sssnic/base/sssnic_hw.h     |    3 +
 drivers/net/sssnic/base/sssnic_misc.h   |    7 +
 drivers/net/sssnic/meson.build          |    2 +
 drivers/net/sssnic/sssnic_ethdev.c      |   12 +
 drivers/net/sssnic/sssnic_ethdev.h      |    1 +
 drivers/net/sssnic/sssnic_ethdev_fdir.c | 1017 +++++++++++++++++++++++
 drivers/net/sssnic/sssnic_ethdev_fdir.h |  332 ++++++++
 drivers/net/sssnic/sssnic_ethdev_flow.c |  981 ++++++++++++++++++++++
 drivers/net/sssnic/sssnic_ethdev_flow.h |   11 +
 drivers/net/sssnic/sssnic_ethdev_rx.c   |   18 +
 14 files changed, 2753 insertions(+)
 create mode 100644 drivers/net/sssnic/sssnic_ethdev_fdir.c
 create mode 100644 drivers/net/sssnic/sssnic_ethdev_fdir.h
 create mode 100644 drivers/net/sssnic/sssnic_ethdev_flow.c
 create mode 100644 drivers/net/sssnic/sssnic_ethdev_flow.h

diff --git a/doc/guides/nics/features/sssnic.ini 
b/doc/guides/nics/features/sssnic.ini
index f5738ac934..57e7440d86 100644
--- a/doc/guides/nics/features/sssnic.ini
+++ b/doc/guides/nics/features/sssnic.ini
@@ -33,3 +33,15 @@ FW version           = Y
 Linux                = Y
 ARMv8                = Y
 x86-64               = Y
+
+[rte_flow items]
+any                  = Y
+eth                  = Y
+ipv4                 = Y
+ipv6                 = Y
+tcp                  = Y
+udp                  = Y
+vxlan                = Y
+
+[rte_flow actions]
+queue                = Y
diff --git a/drivers/net/sssnic/base/sssnic_api.c 
b/drivers/net/sssnic/base/sssnic_api.c
index 68c16c9c1e..0e965442fd 100644
--- a/drivers/net/sssnic/base/sssnic_api.c
+++ b/drivers/net/sssnic/base/sssnic_api.c
@@ -1635,3 +1635,267 @@ sssnic_vlan_filter_set(struct sssnic_hw *hw, uint16_t 
vid, bool add)
 
        return 0;
 }
+
+int
+sssnic_tcam_enable_set(struct sssnic_hw *hw, bool enabled)
+{
+       struct sssnic_tcam_enable_set_cmd cmd;
+       struct sssnic_msg msg;
+       uint32_t cmd_len;
+       int ret;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd_len = sizeof(cmd);
+       cmd.function = SSSNIC_FUNC_IDX(hw);
+       cmd.enabled = enabled ? 1 : 0;
+
+       sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len,
+               SSSNIC_SET_TCAM_ENABLE_CMD, SSSNIC_MPU_FUNC_IDX,
+               SSSNIC_LAN_MODULE, SSSNIC_MSG_TYPE_REQ);
+       ret = sssnic_mbox_send(hw, &msg, (uint8_t *)&cmd, &cmd_len, 0);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to send mbox message, ret=%d", ret);
+               return ret;
+       }
+
+       if (cmd_len == 0 || cmd.common.status != 0) {
+               if (cmd.common.status == SSSNIC_TCAM_CMD_STATUS_UNSUPPORTED)
+                       PMD_DRV_LOG(WARNING,
+                               "SSSNIC_SET_TCAM_ENABLED_CMD is unsupported");
+               else
+                       PMD_DRV_LOG(ERR,
+                               "Bad response to SSSNIC_SET_TCAM_ENABLE_CMD, 
len=%u, status=%u",
+                               cmd_len, cmd.common.status);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+int
+sssnic_tcam_flush(struct sssnic_hw *hw)
+{
+       struct sssnic_tcam_flush_cmd cmd;
+       struct sssnic_msg msg;
+       uint32_t cmd_len;
+       int ret;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd_len = sizeof(cmd);
+       cmd.function = SSSNIC_FUNC_IDX(hw);
+
+       sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len, SSSNIC_FLUSH_TCAM_CMD,
+               SSSNIC_MPU_FUNC_IDX, SSSNIC_LAN_MODULE, SSSNIC_MSG_TYPE_REQ);
+       ret = sssnic_mbox_send(hw, &msg, (uint8_t *)&cmd, &cmd_len, 0);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to send mbox message, ret=%d", ret);
+               return ret;
+       }
+
+       if (cmd_len == 0 || cmd.common.status != 0) {
+               if (cmd.common.status == SSSNIC_TCAM_CMD_STATUS_UNSUPPORTED)
+                       PMD_DRV_LOG(WARNING,
+                               "SSSNIC_FLUSH_TCAM_CMD is unsupported");
+               else
+                       PMD_DRV_LOG(ERR,
+                               "Bad response to SSSNIC_FLUSH_TCAM_CMD, len=%u, 
status=%u",
+                               cmd_len, cmd.common.status);
+               return -EIO;
+       }
+       return 0;
+}
+
+int
+sssnic_tcam_disable_and_flush(struct sssnic_hw *hw)
+{
+       int ret;
+
+       ret = sssnic_tcam_enable_set(hw, 0);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Could not disable TCAM");
+               return ret;
+       }
+
+       ret = sssnic_tcam_flush(hw);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Could not flush TCAM");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int
+sssnic_tcam_block_cfg(struct sssnic_hw *hw, uint8_t flag, uint16_t *block_idx)
+{
+       struct sssnic_tcam_block_cfg_cmd cmd;
+       struct sssnic_msg msg;
+       uint32_t cmd_len;
+       int ret;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd_len = sizeof(cmd);
+       cmd.function = SSSNIC_FUNC_IDX(hw);
+       cmd.flag = flag;
+       if (flag == SSSNIC_TCAM_BLOCK_CFG_CMD_FLAG_FREE)
+               cmd.idx = *block_idx;
+
+       sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len,
+               SSSNIC_TCAM_CFG_BLOCK_CMD, SSSNIC_MPU_FUNC_IDX,
+               SSSNIC_LAN_MODULE, SSSNIC_MSG_TYPE_REQ);
+       ret = sssnic_mbox_send(hw, &msg, (uint8_t *)&cmd, &cmd_len, 0);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to send mbox message, ret=%d", ret);
+               return ret;
+       }
+
+       if (cmd_len == 0 || cmd.common.status != 0) {
+               if (cmd.common.status == SSSNIC_TCAM_CMD_STATUS_UNSUPPORTED)
+                       PMD_DRV_LOG(WARNING,
+                               "SSSNIC_CFG_TCAM_BLOCK_CMD is unsupported");
+               else
+                       PMD_DRV_LOG(ERR,
+                               "Bad response to SSSNIC_CFG_TCAM_BLOCK_CMD, 
len=%u, status=%u",
+                               cmd_len, cmd.common.status);
+               return -EIO;
+       }
+
+       if (flag == SSSNIC_TCAM_BLOCK_CFG_CMD_FLAG_ALLOC)
+               *block_idx = cmd.idx;
+
+       return 0;
+}
+
+int
+sssnic_tcam_block_alloc(struct sssnic_hw *hw, uint16_t *block_idx)
+{
+       if (block_idx == NULL)
+               return -EINVAL;
+
+       return sssnic_tcam_block_cfg(hw, SSSNIC_TCAM_BLOCK_CFG_CMD_FLAG_ALLOC,
+               block_idx);
+}
+
+int
+sssnic_tcam_block_free(struct sssnic_hw *hw, uint16_t block_idx)
+{
+       return sssnic_tcam_block_cfg(hw, SSSNIC_TCAM_BLOCK_CFG_CMD_FLAG_FREE,
+               &block_idx);
+}
+
+int
+sssnic_tcam_packet_type_filter_set(struct sssnic_hw *hw, uint8_t ptype,
+       uint16_t qid, bool enabled)
+{
+       struct sssnic_tcam_ptype_filter_set_cmd cmd;
+       struct sssnic_msg msg;
+       uint32_t cmd_len;
+       int ret;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd_len = sizeof(cmd);
+       cmd.function = SSSNIC_FUNC_IDX(hw);
+       cmd.ptype = ptype;
+       cmd.qid = qid;
+       cmd.enable = enabled ? 1 : 0;
+
+       sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len,
+               SSSNIC_TCAM_SET_PTYPE_FILTER_CMD, SSSNIC_MPU_FUNC_IDX,
+               SSSNIC_LAN_MODULE, SSSNIC_MSG_TYPE_REQ);
+       ret = sssnic_mbox_send(hw, &msg, (uint8_t *)&cmd, &cmd_len, 0);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to send mbox message, ret=%d", ret);
+               return ret;
+       }
+
+       if (cmd_len == 0 || cmd.common.status != 0) {
+               if (cmd.common.status == SSSNIC_TCAM_CMD_STATUS_UNSUPPORTED)
+                       PMD_DRV_LOG(WARNING,
+                               "SSSNIC_TCAM_SET_PTYPE_FILTER_CMD is 
unsupported");
+               else
+                       PMD_DRV_LOG(ERR,
+                               "Bad response to 
SSSNIC_TCAM_SET_PTYPE_FILTER_CMD, len=%u, status=%u",
+                               cmd_len, cmd.common.status);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+int
+sssnic_tcam_entry_add(struct sssnic_hw *hw, struct sssnic_tcam_entry *entry)
+{
+       struct sssnic_tcam_entry_add_cmd cmd;
+       struct sssnic_msg msg;
+       uint32_t cmd_len;
+       int ret;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd_len = sizeof(cmd);
+       cmd.function = SSSNIC_FUNC_IDX(hw);
+       rte_memcpy(&cmd.data, entry, sizeof(cmd.data));
+
+       if (entry->index >= SSSNIC_TCAM_MAX_ENTRY_NUM) {
+               PMD_DRV_LOG(ERR, "Invalid TCAM entry index: %u", entry->index);
+               return -EINVAL;
+       }
+
+       sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len,
+               SSSNIC_ADD_TCAM_ENTRY_CMD, SSSNIC_MPU_FUNC_IDX,
+               SSSNIC_LAN_MODULE, SSSNIC_MSG_TYPE_REQ);
+       ret = sssnic_mbox_send(hw, &msg, (uint8_t *)&cmd, &cmd_len, 0);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to send mbox message, ret=%d", ret);
+               return ret;
+       }
+
+       if (cmd_len == 0 || cmd.common.status != 0) {
+               if (cmd.common.status == SSSNIC_TCAM_CMD_STATUS_UNSUPPORTED)
+                       PMD_DRV_LOG(WARNING,
+                               "SSSNIC_ADD_TCAM_ENTRY_CMD is unsupported");
+               else
+                       PMD_DRV_LOG(ERR,
+                               "Bad response to SSSNIC_ADD_TCAM_ENTRY_CMD, 
len=%u, status=%u",
+                               cmd_len, cmd.common.status);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+int
+sssnic_tcam_entry_del(struct sssnic_hw *hw, uint32_t entry_idx)
+{
+       struct sssnic_tcam_entry_del_cmd cmd;
+       struct sssnic_msg msg;
+       uint32_t cmd_len;
+       int ret;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd_len = sizeof(cmd);
+       cmd.function = SSSNIC_FUNC_IDX(hw);
+       cmd.start = entry_idx;
+       cmd.num = 1;
+
+       sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len,
+               SSSNIC_DEL_TCAM_ENTRY_CMD, SSSNIC_MPU_FUNC_IDX,
+               SSSNIC_LAN_MODULE, SSSNIC_MSG_TYPE_REQ);
+       ret = sssnic_mbox_send(hw, &msg, (uint8_t *)&cmd, &cmd_len, 0);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to send mbox message, ret=%d", ret);
+               return ret;
+       }
+
+       if (cmd_len == 0 || cmd.common.status != 0) {
+               if (cmd.common.status == SSSNIC_TCAM_CMD_STATUS_UNSUPPORTED)
+                       PMD_DRV_LOG(WARNING,
+                               "SSSNIC_ADD_TCAM_ENTRY_CMD is unsupported");
+               else
+                       PMD_DRV_LOG(ERR,
+                               "Bad response to SSSNIC_ADD_TCAM_ENTRY_CMD, 
len=%u, status=%u",
+                               cmd_len, cmd.common.status);
+               return -EIO;
+       }
+
+       return 0;
+}
diff --git a/drivers/net/sssnic/base/sssnic_api.h 
b/drivers/net/sssnic/base/sssnic_api.h
index 28b235dda2..7a02ec61ee 100644
--- a/drivers/net/sssnic/base/sssnic_api.h
+++ b/drivers/net/sssnic/base/sssnic_api.h
@@ -409,6 +409,18 @@ struct sssnic_fw_version {
        char time[SSSNIC_FW_VERSION_LEN];
 };
 
+struct sssnic_tcam_entry {
+       uint32_t index;
+       struct {
+               uint32_t qid;
+               uint32_t resvd;
+       } result;
+       struct {
+               uint8_t data0[SSSNIC_TCAM_KEY_SIZE];
+               uint8_t data1[SSSNIC_TCAM_KEY_SIZE];
+       } key;
+};
+
 int sssnic_msix_attr_get(struct sssnic_hw *hw, uint16_t msix_idx,
        struct sssnic_msix_attr *attr);
 int sssnic_msix_attr_set(struct sssnic_hw *hw, uint16_t msix_idx,
@@ -470,5 +482,15 @@ int sssnic_flow_ctrl_set(struct sssnic_hw *hw, bool 
autoneg, bool rx_en,
 int sssnic_flow_ctrl_get(struct sssnic_hw *hw, bool *autoneg, bool *rx_en,
        bool *tx_en);
 int sssnic_vlan_filter_set(struct sssnic_hw *hw, uint16_t vid, bool add);
+int sssnic_tcam_enable_set(struct sssnic_hw *hw, bool enabled);
+int sssnic_tcam_flush(struct sssnic_hw *hw);
+int sssnic_tcam_disable_and_flush(struct sssnic_hw *hw);
+int sssnic_tcam_block_alloc(struct sssnic_hw *hw, uint16_t *block_idx);
+int sssnic_tcam_block_free(struct sssnic_hw *hw, uint16_t block_idx);
+int sssnic_tcam_packet_type_filter_set(struct sssnic_hw *hw, uint8_t ptype,
+       uint16_t qid, bool enabled);
+int sssnic_tcam_entry_add(struct sssnic_hw *hw,
+       struct sssnic_tcam_entry *entry);
+int sssnic_tcam_entry_del(struct sssnic_hw *hw, uint32_t entry_idx);
 
 #endif /* _SSSNIC_API_H_ */
diff --git a/drivers/net/sssnic/base/sssnic_cmd.h 
b/drivers/net/sssnic/base/sssnic_cmd.h
index 3e70d0e223..c75cb0dad3 100644
--- a/drivers/net/sssnic/base/sssnic_cmd.h
+++ b/drivers/net/sssnic/base/sssnic_cmd.h
@@ -75,6 +75,16 @@ enum sssnic_rss_cmd_id {
        SSSNIC_SET_RSS_TYPE_CMD = 65,
 };
 
+#define SSSNIC_TCAM_CMD_STATUS_UNSUPPORTED 0xff
+enum sssnic_tcam_cmd_id {
+       SSSNIC_ADD_TCAM_ENTRY_CMD = 80,
+       SSSNIC_DEL_TCAM_ENTRY_CMD = 81,
+       SSSNIC_FLUSH_TCAM_CMD = 83,
+       SSSNIC_TCAM_CFG_BLOCK_CMD = 84,
+       SSSNIC_SET_TCAM_ENABLE_CMD = 85,
+       SSSNIC_TCAM_SET_PTYPE_FILTER_CMD = 91,
+};
+
 struct sssnic_cmd_common {
        uint8_t status;
        uint8_t version;
@@ -434,4 +444,65 @@ struct sssnic_vlan_filter_set_cmd {
        uint16_t resvd1;
 };
 
+struct sssnic_tcam_enable_set_cmd {
+       struct sssnic_cmd_common common;
+       uint16_t function;
+       uint8_t enabled;
+       uint8_t resvd[5];
+};
+
+struct sssnic_tcam_flush_cmd {
+       struct sssnic_cmd_common common;
+       uint16_t function;
+       uint16_t resvd;
+};
+
+#define SSSNIC_TCAM_BLOCK_CFG_CMD_FLAG_ALLOC 1
+#define SSSNIC_TCAM_BLOCK_CFG_CMD_FLAG_FREE 0
+struct sssnic_tcam_block_cfg_cmd {
+       struct sssnic_cmd_common common;
+       uint16_t function;
+       uint8_t flag; /* SSSNIC_TCAM_BLOCK_CFG_CMD_FLAG_XX */
+       uint8_t type;
+       uint16_t idx;
+       uint16_t resvd;
+};
+
+struct sssnic_tcam_ptype_filter_set_cmd {
+       struct sssnic_cmd_common common;
+       uint16_t function;
+       uint16_t resvd0;
+       uint8_t enable;
+       uint8_t ptype;
+       uint8_t qid;
+       uint8_t resvd1;
+};
+
+struct sssnic_tcam_entry_add_cmd {
+       struct sssnic_cmd_common common;
+       uint16_t function;
+       uint8_t type;
+       uint8_t resv;
+       struct {
+               uint32_t index;
+               struct {
+                       uint32_t qid;
+                       uint32_t resvd;
+               } result;
+               struct {
+                       uint8_t d0[SSSNIC_TCAM_KEY_SIZE];
+                       uint8_t d1[SSSNIC_TCAM_KEY_SIZE];
+               } key;
+       } data;
+};
+
+struct sssnic_tcam_entry_del_cmd {
+       struct sssnic_cmd_common common;
+       uint16_t function;
+       uint8_t type;
+       uint8_t resv;
+       uint32_t start; /* start index of entry to be deleted */
+       uint32_t num; /* number of entries to be deleted */
+};
+
 #endif /* _SSSNIC_CMD_H_ */
diff --git a/drivers/net/sssnic/base/sssnic_hw.h 
b/drivers/net/sssnic/base/sssnic_hw.h
index 4820212543..6a2d980d5a 100644
--- a/drivers/net/sssnic/base/sssnic_hw.h
+++ b/drivers/net/sssnic/base/sssnic_hw.h
@@ -96,6 +96,9 @@ enum sssnic_module {
        SSSNIC_NETIF_MODULE = 14,
 };
 
+#define SSSNIC_TCAM_KEY_SIZE 44
+#define SSSNIC_TCAM_MAX_ENTRY_NUM 4096
+
 int sssnic_hw_init(struct sssnic_hw *hw);
 void sssnic_hw_shutdown(struct sssnic_hw *hw);
 void sssnic_msix_state_set(struct sssnic_hw *hw, uint16_t msix_id, int state);
diff --git a/drivers/net/sssnic/base/sssnic_misc.h 
b/drivers/net/sssnic/base/sssnic_misc.h
index e30691caef..a1e268710e 100644
--- a/drivers/net/sssnic/base/sssnic_misc.h
+++ b/drivers/net/sssnic/base/sssnic_misc.h
@@ -42,4 +42,11 @@ sssnic_mem_be_to_cpu_32(void *in, void *out, int size)
        }
 }
 
+static inline bool
+sssnic_is_zero_ipv6_addr(const void *ipv6_addr)
+{
+       const uint64_t *ddw = ipv6_addr;
+       return ddw[0] == 0 && ddw[1] == 0;
+}
+
 #endif /* _SSSNIC_MISC_H_ */
diff --git a/drivers/net/sssnic/meson.build b/drivers/net/sssnic/meson.build
index 3541b75c30..03d60f08ec 100644
--- a/drivers/net/sssnic/meson.build
+++ b/drivers/net/sssnic/meson.build
@@ -23,4 +23,6 @@ sources = files(
         'sssnic_ethdev_tx.c',
         'sssnic_ethdev_stats.c',
         'sssnic_ethdev_rss.c',
+        'sssnic_ethdev_fdir.c',
+        'sssnic_ethdev_flow.c',
 )
diff --git a/drivers/net/sssnic/sssnic_ethdev.c 
b/drivers/net/sssnic/sssnic_ethdev.c
index 8a1ccff70b..545833fb55 100644
--- a/drivers/net/sssnic/sssnic_ethdev.c
+++ b/drivers/net/sssnic/sssnic_ethdev.c
@@ -14,6 +14,8 @@
 #include "sssnic_ethdev_tx.h"
 #include "sssnic_ethdev_stats.h"
 #include "sssnic_ethdev_rss.h"
+#include "sssnic_ethdev_fdir.h"
+#include "sssnic_ethdev_flow.h"
 
 static int sssnic_ethdev_init(struct rte_eth_dev *ethdev);
 static void sssnic_ethdev_vlan_filter_clean(struct rte_eth_dev *ethdev);
@@ -345,6 +347,7 @@ sssnic_ethdev_release(struct rte_eth_dev *ethdev)
        sssnic_ethdev_link_intr_disable(ethdev);
        sssnic_ethdev_tx_queue_all_release(ethdev);
        sssnic_ethdev_rx_queue_all_release(ethdev);
+       sssnic_ethdev_fdir_shutdown(ethdev);
        sssnic_ethdev_mac_addrs_clean(ethdev);
        sssnic_hw_shutdown(hw);
        rte_free(hw);
@@ -951,6 +954,7 @@ static const struct eth_dev_ops sssnic_ethdev_ops = {
        .flow_ctrl_get = sssnic_ethdev_flow_ctrl_get,
        .vlan_offload_set = sssnic_ethdev_vlan_offload_set,
        .vlan_filter_set = sssnic_ethdev_vlan_filter_set,
+       .flow_ops_get = sssnic_ethdev_flow_ops_get,
 };
 
 static int
@@ -991,6 +995,12 @@ sssnic_ethdev_init(struct rte_eth_dev *ethdev)
                goto mac_addrs_init_fail;
        }
 
+       ret = sssnic_ethdev_fdir_init(ethdev);
+       if (ret) {
+               PMD_DRV_LOG(ERR, "Failed to initialize fdir info");
+               goto fdir_init_fail;
+       }
+
        netdev->max_num_rxq = SSSNIC_MAX_NUM_RXQ(hw);
        netdev->max_num_txq = SSSNIC_MAX_NUM_TXQ(hw);
 
@@ -1001,6 +1011,8 @@ sssnic_ethdev_init(struct rte_eth_dev *ethdev)
 
        return 0;
 
+fdir_init_fail:
+       sssnic_ethdev_mac_addrs_clean(ethdev);
 mac_addrs_init_fail:
        sssnic_hw_shutdown(0);
        return ret;
diff --git a/drivers/net/sssnic/sssnic_ethdev.h 
b/drivers/net/sssnic/sssnic_ethdev.h
index f19b2bd88f..0ca933b53b 100644
--- a/drivers/net/sssnic/sssnic_ethdev.h
+++ b/drivers/net/sssnic/sssnic_ethdev.h
@@ -82,6 +82,7 @@ struct sssnic_netdev {
        void *hw;
        struct rte_ether_addr *mcast_addrs;
        struct rte_ether_addr default_addr;
+       struct sssnic_ethdev_fdir_info *fdir_info;
        uint16_t max_num_txq;
        uint16_t max_num_rxq;
        uint16_t num_started_rxqs;
diff --git a/drivers/net/sssnic/sssnic_ethdev_fdir.c 
b/drivers/net/sssnic/sssnic_ethdev_fdir.c
new file mode 100644
index 0000000000..cec9fb219f
--- /dev/null
+++ b/drivers/net/sssnic/sssnic_ethdev_fdir.c
@@ -0,0 +1,1017 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2022 Shenzhen 3SNIC Information Technology Co., Ltd.
+ */
+
+#include <rte_common.h>
+#include <rte_tailq.h>
+#include <ethdev_pci.h>
+
+#include "sssnic_log.h"
+#include "sssnic_ethdev.h"
+#include "sssnic_ethdev_fdir.h"
+#include "base/sssnic_hw.h"
+#include "base/sssnic_api.h"
+
+#define SSSNIC_NETDEV_FDIR_INFO(netdev) ((netdev)->fdir_info)
+#define SSSNIC_ETHDEV_FDIR_INFO(ethdev)                                        
\
+       (SSSNIC_NETDEV_FDIR_INFO(SSSNIC_ETHDEV_PRIVATE(ethdev)))
+
+enum {
+       SSSNIC_ETHDEV_PTYPE_INVAL = 0,
+       SSSNIC_ETHDEV_PTYPE_ARP = 1,
+       SSSNIC_ETHDEV_PTYPE_ARP_REQ = 2,
+       SSSNIC_ETHDEV_PTYPE_ARP_REP = 3,
+       SSSNIC_ETHDEV_PTYPE_RARP = 4,
+       SSSNIC_ETHDEV_PTYPE_LACP = 5,
+       SSSNIC_ETHDEV_PTYPE_LLDP = 6,
+       SSSNIC_ETHDEV_PTYPE_OAM = 7,
+       SSSNIC_ETHDEV_PTYPE_CDCP = 8,
+       SSSNIC_ETHDEV_PTYPE_CNM = 9,
+       SSSNIC_ETHDEV_PTYPE_ECP = 10,
+};
+
+#define SSSNIC_ETHDEV_TCAM_ENTRY_INVAL_IDX 0xffff
+struct sssnic_ethdev_fdir_entry {
+       TAILQ_ENTRY(sssnic_ethdev_fdir_entry) node;
+       struct sssnic_ethdev_tcam_block *tcam_block;
+       uint32_t tcam_entry_idx;
+       int enabled;
+       struct sssnic_ethdev_fdir_rule *rule;
+};
+
+#define SSSNIC_ETHDEV_TCAM_BLOCK_SZ 16
+struct sssnic_ethdev_tcam_block {
+       TAILQ_ENTRY(sssnic_ethdev_tcam_block) node;
+       uint16_t id;
+       uint16_t used_entries;
+       uint8_t entries_status[SSSNIC_ETHDEV_TCAM_BLOCK_SZ]; /* 0: IDLE, 1: 
USED */
+};
+
+struct sssnic_ethdev_tcam {
+       TAILQ_HEAD(, sssnic_ethdev_tcam_block) block_list;
+       uint16_t num_blocks;
+       uint16_t used_entries; /* Count of used entries */
+       int enabled;
+};
+
+struct sssnic_ethdev_fdir_info {
+       struct rte_eth_dev *ethdev;
+       struct sssnic_ethdev_tcam tcam;
+       uint32_t num_entries;
+       TAILQ_HEAD(, sssnic_ethdev_fdir_entry) ethertype_entry_list;
+       TAILQ_HEAD(, sssnic_ethdev_fdir_entry) flow_entry_list;
+};
+
+static int
+sssnic_ethdev_tcam_init(struct rte_eth_dev *ethdev)
+{
+       struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev);
+       struct sssnic_ethdev_fdir_info *fdir_info;
+
+       fdir_info = SSSNIC_ETHDEV_FDIR_INFO(ethdev);
+       TAILQ_INIT(&fdir_info->tcam.block_list);
+
+       sssnic_tcam_disable_and_flush(hw);
+
+       return 0;
+}
+
+static void
+sssnic_ethdev_tcam_shutdown(struct rte_eth_dev *ethdev)
+{
+       struct sssnic_ethdev_fdir_info *fdir_info;
+       struct sssnic_ethdev_tcam *tcam;
+       struct sssnic_ethdev_tcam_block *block, *tmp;
+
+       fdir_info = SSSNIC_ETHDEV_FDIR_INFO(ethdev);
+       tcam = &fdir_info->tcam;
+
+       RTE_TAILQ_FOREACH_SAFE(block, &tcam->block_list, node, tmp)
+       {
+               TAILQ_REMOVE(&tcam->block_list, block, node);
+               rte_free(block);
+       }
+}
+
+static int
+sssnic_ethdev_tcam_enable(struct rte_eth_dev *ethdev)
+{
+       struct sssnic_ethdev_fdir_info *fdir_info;
+       struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev);
+       int ret;
+
+       fdir_info = SSSNIC_ETHDEV_FDIR_INFO(ethdev);
+
+       if (!fdir_info->tcam.enabled) {
+               ret = sssnic_tcam_enable_set(hw, 1);
+               if (ret != 0) {
+                       PMD_DRV_LOG(ERR, "Failed to enable TCAM");
+                       return ret;
+               }
+
+               fdir_info->tcam.enabled = 1;
+       }
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_tcam_disable(struct rte_eth_dev *ethdev)
+{
+       struct sssnic_ethdev_fdir_info *fdir_info;
+       struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev);
+       int ret;
+
+       fdir_info = SSSNIC_ETHDEV_FDIR_INFO(ethdev);
+
+       if (fdir_info->tcam.enabled) {
+               ret = sssnic_tcam_enable_set(hw, 0);
+               if (ret != 0) {
+                       PMD_DRV_LOG(ERR, "Failed to enable TCAM");
+                       return ret;
+               }
+
+               fdir_info->tcam.enabled = 0;
+       }
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_tcam_block_alloc(struct rte_eth_dev *ethdev,
+       struct sssnic_ethdev_tcam_block **block)
+{
+       struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev);
+       struct sssnic_ethdev_fdir_info *fdir_info =
+               SSSNIC_ETHDEV_FDIR_INFO(ethdev);
+       struct sssnic_ethdev_tcam_block *new;
+       int ret;
+
+       new = rte_zmalloc("sssnic_tcam_block", sizeof(*new), 0);
+       if (new == NULL) {
+               PMD_DRV_LOG(ERR,
+                       "Failed to allocate memory for tcam block struct!");
+               return -ENOMEM;
+       }
+
+       ret = sssnic_tcam_block_alloc(hw, &new->id);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to alloc tcam block!");
+               rte_free(new);
+               return ret;
+       }
+
+       TAILQ_INSERT_HEAD(&fdir_info->tcam.block_list, new, node);
+       fdir_info->tcam.num_blocks++;
+
+       if (block != NULL)
+               *block = new;
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_tcam_block_free(struct rte_eth_dev *ethdev,
+       struct sssnic_ethdev_tcam_block *block)
+{
+       struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev);
+       struct sssnic_ethdev_fdir_info *fdir_info =
+               SSSNIC_ETHDEV_FDIR_INFO(ethdev);
+       int ret;
+
+       ret = sssnic_tcam_block_free(hw, block->id);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to free tcam block:%u!", block->id);
+               return ret;
+       }
+
+       TAILQ_REMOVE(&fdir_info->tcam.block_list, block, node);
+       fdir_info->tcam.num_blocks--;
+       rte_free(block);
+
+       return 0;
+}
+
+static struct sssnic_ethdev_tcam_block *
+sssnic_ethdev_available_tcam_block_lookup(struct sssnic_ethdev_tcam *tcam)
+{
+       struct sssnic_ethdev_tcam_block *block;
+
+       TAILQ_FOREACH(block, &tcam->block_list, node)
+       {
+               if (block->used_entries < SSSNIC_ETHDEV_TCAM_BLOCK_SZ)
+                       return block;
+       }
+
+       return NULL;
+}
+
+static int
+sssnic_ethdev_tcam_block_entry_alloc(struct sssnic_ethdev_tcam_block *block,
+       uint32_t *entry_idx)
+{
+       uint32_t i;
+
+       for (i = 0; i < SSSNIC_ETHDEV_TCAM_BLOCK_SZ; i++) {
+               if (block->entries_status[i] == 0) {
+                       *entry_idx = i;
+                       block->entries_status[i] = 1;
+                       block->used_entries++;
+                       return 0;
+               }
+       }
+
+       return -ENOMEM;
+}
+
+static int
+sssnic_ethdev_tcam_block_entry_free(struct sssnic_ethdev_tcam_block *block,
+       uint32_t entry_idx)
+{
+       if (block != NULL && entry_idx < SSSNIC_ETHDEV_TCAM_BLOCK_SZ) {
+               if (block->entries_status[entry_idx] == 1) {
+                       block->entries_status[entry_idx] = 0;
+                       block->used_entries--;
+                       return 0; /* found and freed */
+               }
+       }
+       return -1; /* not found */
+}
+
+static int
+sssnic_ethdev_tcam_entry_alloc(struct rte_eth_dev *ethdev,
+       struct sssnic_ethdev_tcam_block **block, uint32_t *entry_idx)
+{
+       struct sssnic_ethdev_fdir_info *fdir_info =
+               SSSNIC_ETHDEV_FDIR_INFO(ethdev);
+       struct sssnic_ethdev_tcam *tcam;
+       struct sssnic_ethdev_tcam_block *tcam_block;
+       int new_block = 0;
+       uint32_t eid;
+       int ret;
+
+       tcam = &fdir_info->tcam;
+
+       if (tcam->num_blocks == 0 ||
+               tcam->used_entries >=
+                       tcam->num_blocks * SSSNIC_ETHDEV_TCAM_BLOCK_SZ) {
+               ret = sssnic_ethdev_tcam_block_alloc(ethdev, &tcam_block);
+               if (ret != 0) {
+                       PMD_DRV_LOG(ERR,
+                               "No TCAM memory, used block count: %u, used 
entries count:%u",
+                               tcam->num_blocks, tcam->used_entries);
+                       return ret;
+               }
+               new_block = 1;
+       } else {
+               tcam_block = sssnic_ethdev_available_tcam_block_lookup(tcam);
+               if (tcam_block == NULL) {
+                       PMD_DRV_LOG(CRIT,
+                               "No available TCAM block, used block count:%u, 
used entries count:%u",
+                               tcam->num_blocks, tcam->used_entries);
+                       return -ENOMEM;
+               }
+       }
+
+       ret = sssnic_ethdev_tcam_block_entry_alloc(tcam_block, &eid);
+       if (ret != 0) {
+               PMD_DRV_LOG(CRIT,
+                       "No available entry in TCAM block, block idx:%u, used 
entries:%u",
+                       tcam_block->id, tcam_block->used_entries);
+               if (unlikely(new_block))
+                       sssnic_ethdev_tcam_block_free(ethdev, tcam_block);
+
+               return -ENOMEM;
+       }
+
+       tcam->used_entries++;
+
+       *block = tcam_block;
+       *entry_idx = eid;
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_tcam_entry_free(struct rte_eth_dev *ethdev,
+       struct sssnic_ethdev_tcam_block *tcam_block, uint32_t entry_idx)
+{
+       int ret;
+       struct sssnic_ethdev_fdir_info *fdir_info =
+               SSSNIC_ETHDEV_FDIR_INFO(ethdev);
+       struct sssnic_ethdev_tcam *tcam;
+
+       tcam = &fdir_info->tcam;
+
+       ret = sssnic_ethdev_tcam_block_entry_free(tcam_block, entry_idx);
+       if (ret != 0)
+               return 0; /* not found was considered as success */
+
+       if (tcam_block->used_entries == 0) {
+               ret = sssnic_ethdev_tcam_block_free(ethdev, tcam_block);
+               if (ret != 0)
+                       PMD_DRV_LOG(ERR, "Failed to free TCAM block:%u",
+                               tcam_block->id);
+       }
+
+       tcam->used_entries--;
+       return 0;
+}
+
+static void
+sssnic_ethdev_tcam_entry_init(struct sssnic_ethdev_fdir_flow_match *flow,
+       struct sssnic_tcam_entry *entry)
+{
+       uint8_t i;
+       uint8_t *flow_key;
+       uint8_t *flow_mask;
+
+       flow_key = (uint8_t *)&flow->key;
+       flow_mask = (uint8_t *)&flow->mask;
+
+       for (i = 0; i < sizeof(entry->key.data0); i++) {
+               entry->key.data1[i] = flow_key[i] & flow_mask[i];
+               entry->key.data0[i] =
+                       entry->key.data1[i] ^ flow_mask[i];
+       }
+}
+
+
+static struct sssnic_ethdev_fdir_entry *
+sssnic_ethdev_fdir_entry_lookup(struct sssnic_ethdev_fdir_info *fdir_info,
+       struct sssnic_ethdev_fdir_rule *rule)
+{
+       struct sssnic_ethdev_fdir_entry *e;
+       struct sssnic_ethdev_fdir_match *m;
+       struct sssnic_ethdev_fdir_match *match = &rule->match;
+
+       /* fast lookup */
+       if (rule->cookie != NULL)
+               return (struct sssnic_ethdev_fdir_entry *)rule->cookie;
+
+       if (rule->match.type == SSSNIC_ETHDEV_FDIR_MATCH_FLOW) {
+               TAILQ_FOREACH(e, &fdir_info->flow_entry_list, node)
+               {
+                       m = &e->rule->match;
+                       if (memcmp(&match->flow, &m->flow, sizeof(m->flow)) ==
+                               0)
+                               return e;
+               }
+       } else if (rule->match.type == SSSNIC_ETHDEV_FDIR_MATCH_ETHERTYPE) {
+               TAILQ_FOREACH(e, &fdir_info->ethertype_entry_list, node)
+               {
+                       m = &e->rule->match;
+                       if (match->ethertype.key.ether_type ==
+                               m->ethertype.key.ether_type)
+                               return e;
+               }
+       }
+
+       return NULL;
+}
+
+static inline void
+sssnic_ethdev_fdir_entry_add(struct sssnic_ethdev_fdir_info *fdir_info,
+       struct sssnic_ethdev_fdir_entry *entry)
+{
+       if (entry->rule->match.type == SSSNIC_ETHDEV_FDIR_MATCH_ETHERTYPE)
+               TAILQ_INSERT_TAIL(&fdir_info->ethertype_entry_list, entry,
+                       node);
+       else
+               TAILQ_INSERT_TAIL(&fdir_info->flow_entry_list, entry, node);
+
+       fdir_info->num_entries++;
+}
+
+static inline void
+sssnic_ethdev_fdir_entry_del(struct sssnic_ethdev_fdir_info *fdir_info,
+       struct sssnic_ethdev_fdir_entry *entry)
+{
+       if (entry->rule->match.type == SSSNIC_ETHDEV_FDIR_MATCH_ETHERTYPE)
+               TAILQ_REMOVE(&fdir_info->ethertype_entry_list, entry, node);
+       else
+               TAILQ_REMOVE(&fdir_info->flow_entry_list, entry, node);
+
+       fdir_info->num_entries--;
+}
+
+static int
+sssnic_ethdev_fdir_arp_pkt_filter_set(struct rte_eth_dev *ethdev, uint16_t qid,
+       int enabled)
+{
+       struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev);
+       int ret;
+
+       ret = sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_ARP,
+               qid, enabled);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to %s ARP packet filter!",
+                       enabled ? "enable" : "disable");
+               return ret;
+       }
+
+       ret = sssnic_tcam_packet_type_filter_set(hw,
+               SSSNIC_ETHDEV_PTYPE_ARP_REQ, qid, enabled);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to %s ARP request packet filter!",
+                       enabled ? "enable" : "disable");
+               goto set_arp_req_fail;
+       }
+
+       ret = sssnic_tcam_packet_type_filter_set(hw,
+               SSSNIC_ETHDEV_PTYPE_ARP_REP, qid, enabled);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to %s ARP reply packet filter!",
+                       enabled ? "enable" : "disable");
+               goto set_arp_rep_fail;
+       }
+
+       return 0;
+
+set_arp_rep_fail:
+       sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_ARP_REQ, qid,
+               !enabled);
+set_arp_req_fail:
+       sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_ARP, qid,
+               !enabled);
+
+       return ret;
+}
+
+static int
+sssnic_ethdev_fdir_slow_pkt_filter_set(struct rte_eth_dev *ethdev, uint16_t 
qid,
+       int enabled)
+{
+       struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev);
+       int ret;
+
+       ret = sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_LACP,
+               qid, enabled);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to %s LACP packet filter!",
+                       enabled ? "enable" : "disable");
+               return ret;
+       }
+
+       ret = sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_OAM,
+               qid, enabled);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to %s OAM packet filter!",
+                       enabled ? "enable" : "disable");
+
+               sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_LACP,
+                       qid, !enabled);
+       }
+
+       return ret;
+}
+
+static int
+sssnic_ethdev_fdir_lldp_pkt_filter_set(struct rte_eth_dev *ethdev, uint16_t 
qid,
+       int enabled)
+{
+       struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev);
+       int ret;
+
+       ret = sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_LLDP,
+               qid, enabled);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to %s LLDP packet filter!",
+                       enabled ? "enable" : "disable");
+               return ret;
+       }
+
+       ret = sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_CDCP,
+               qid, enabled);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to %s CDCP packet filter!",
+                       enabled ? "enable" : "disable");
+
+               sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_LLDP,
+                       qid, !enabled);
+       }
+
+       return ret;
+}
+
+static int
+sssnic_ethdev_fdir_pkt_filter_set(struct rte_eth_dev *ethdev,
+       uint16_t ether_type, uint16_t qid, int enabled)
+{
+       int ret;
+       struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev);
+
+       switch (ether_type) {
+       case RTE_ETHER_TYPE_ARP:
+               ret = sssnic_ethdev_fdir_arp_pkt_filter_set(ethdev, qid,
+                       enabled);
+               break;
+       case RTE_ETHER_TYPE_RARP:
+               ret = sssnic_tcam_packet_type_filter_set(hw,
+                       SSSNIC_ETHDEV_PTYPE_RARP, qid, enabled);
+               break;
+       case RTE_ETHER_TYPE_SLOW:
+               ret = sssnic_ethdev_fdir_slow_pkt_filter_set(ethdev, qid,
+                       enabled);
+               break;
+       case RTE_ETHER_TYPE_LLDP:
+               ret = sssnic_ethdev_fdir_lldp_pkt_filter_set(ethdev, qid,
+                       enabled);
+               break;
+       case 0x22e7: /* CNM ether type */
+               ret = sssnic_tcam_packet_type_filter_set(hw,
+                       SSSNIC_ETHDEV_PTYPE_CNM, qid, enabled);
+               break;
+       case 0x8940: /* ECP ether type */
+               ret = sssnic_tcam_packet_type_filter_set(hw,
+                       SSSNIC_ETHDEV_PTYPE_ECP, qid, enabled);
+               break;
+       default:
+               PMD_DRV_LOG(ERR, "Ethertype 0x%x is not supported to filter!",
+                       ether_type);
+               return -EINVAL;
+       }
+
+       if (ret != 0)
+               PMD_DRV_LOG(ERR, "Failed to %s filter for ether type: %x.",
+                       enabled ? "enable" : "disable", ether_type);
+
+       return ret;
+}
+
+static inline struct sssnic_ethdev_fdir_entry *
+sssnic_ethdev_fdir_entry_alloc(void)
+{
+       struct sssnic_ethdev_fdir_entry *e;
+
+       e = rte_zmalloc("sssnic_fdir_entry", sizeof(*e), 0);
+       if (e != NULL)
+               e->tcam_entry_idx = SSSNIC_ETHDEV_TCAM_ENTRY_INVAL_IDX;
+       else
+               PMD_DRV_LOG(ERR,
+                       "Failed to allocate memory for fdir entry struct!");
+
+       return e;
+}
+
+static inline void
+sssnic_ethdev_fdir_entry_free(struct sssnic_ethdev_fdir_entry *e)
+{
+       if (e != NULL)
+               rte_free(e);
+}
+
+/* Apply fdir rule to HW */
+static int
+sssnic_ethdev_fdir_entry_enable(struct rte_eth_dev *ethdev,
+       struct sssnic_ethdev_fdir_entry *entry)
+{
+       struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev);
+       struct sssnic_tcam_entry tcam_entry;
+       int ret;
+
+       if (unlikely(entry->rule == NULL)) {
+               PMD_DRV_LOG(ERR, "fdir rule is null!");
+               return -EINVAL;
+       }
+
+       if (entry->enabled)
+               return 0;
+
+       if (entry->tcam_entry_idx != SSSNIC_ETHDEV_TCAM_ENTRY_INVAL_IDX) {
+               memset(&tcam_entry, 0, sizeof(tcam_entry));
+               sssnic_ethdev_tcam_entry_init(&entry->rule->match.flow,
+                       &tcam_entry);
+               tcam_entry.result.qid = entry->rule->action.qid;
+               tcam_entry.index =
+                       entry->tcam_entry_idx +
+                       (entry->tcam_block->id * SSSNIC_ETHDEV_TCAM_BLOCK_SZ);
+
+               ret = sssnic_tcam_entry_add(hw, &tcam_entry);
+               if (ret != 0)
+                       PMD_DRV_LOG(ERR,
+                               "Failed to add TCAM entry, block:%u, entry:%u, 
tcam_entry:%u",
+                               entry->tcam_block->id, entry->tcam_entry_idx,
+                               tcam_entry.index);
+
+       } else {
+               ret = sssnic_ethdev_fdir_pkt_filter_set(ethdev,
+                       entry->rule->match.ethertype.key.ether_type,
+                       entry->rule->action.qid, 1);
+               if (ret != 0)
+                       PMD_DRV_LOG(ERR, "Failed to enable ethertype(%x) 
filter",
+                               entry->rule->match.ethertype.key.ether_type);
+       }
+
+       entry->enabled = 1;
+
+       return ret;
+}
+
+/* remove fdir rule from HW */
+static int
+sssnic_ethdev_fdir_entry_disable(struct rte_eth_dev *ethdev,
+       struct sssnic_ethdev_fdir_entry *entry)
+{
+       struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev);
+       uint32_t tcam_entry_idx;
+       int ret;
+
+       if (unlikely(entry->rule == NULL)) {
+               PMD_DRV_LOG(ERR, "fdir rule is null!");
+               return -EINVAL;
+       }
+
+       if (!entry->enabled)
+               return 0;
+
+       if (entry->tcam_entry_idx != SSSNIC_ETHDEV_TCAM_ENTRY_INVAL_IDX) {
+               tcam_entry_idx =
+                       entry->tcam_entry_idx +
+                       (entry->tcam_block->id * SSSNIC_ETHDEV_TCAM_BLOCK_SZ);
+
+               ret = sssnic_tcam_entry_del(hw, tcam_entry_idx);
+               if (ret != 0) {
+                       PMD_DRV_LOG(ERR,
+                               "Failed to del TCAM entry, block:%u, entry:%u",
+                               entry->tcam_block->id, entry->tcam_entry_idx);
+                       return ret;
+               }
+       } else {
+               ret = sssnic_ethdev_fdir_pkt_filter_set(ethdev,
+                       entry->rule->match.ethertype.key.ether_type,
+                       entry->rule->action.qid, 0);
+               if (ret != 0) {
+                       PMD_DRV_LOG(ERR,
+                               "Failed to disable ethertype(%x) filter",
+                               entry->rule->match.ethertype.key.ether_type);
+                       return ret;
+               }
+       }
+
+       entry->enabled = 0;
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_fdir_ethertype_rule_add(struct sssnic_ethdev_fdir_info 
*fdir_info,
+       struct sssnic_ethdev_fdir_rule *rule)
+{
+       struct sssnic_ethdev_fdir_entry *fdir_entry;
+       int ret;
+
+       fdir_entry = sssnic_ethdev_fdir_entry_alloc();
+       if (fdir_entry == NULL)
+               return -ENOMEM;
+
+       fdir_entry->rule = rule;
+
+       ret = sssnic_ethdev_fdir_entry_enable(fdir_info->ethdev, fdir_entry);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to enable ethertype(%u) entry",
+                       rule->match.ethertype.key.ether_type);
+
+               sssnic_ethdev_fdir_entry_free(fdir_entry);
+
+               return ret;
+       }
+
+       rule->cookie = fdir_entry;
+       sssnic_ethdev_fdir_entry_add(fdir_info, fdir_entry);
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_fdir_ethertype_rule_del(struct sssnic_ethdev_fdir_info 
*fdir_info,
+       struct sssnic_ethdev_fdir_rule *rule)
+{
+       struct sssnic_ethdev_fdir_entry *fdir_entry;
+       int ret;
+
+       fdir_entry = (struct sssnic_ethdev_fdir_entry *)rule->cookie;
+
+       ret = sssnic_ethdev_fdir_entry_disable(fdir_info->ethdev, fdir_entry);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to disable ethertype(%u) entry",
+                       rule->match.ethertype.key.ether_type);
+               return ret;
+       }
+
+       rule->cookie = NULL;
+       sssnic_ethdev_fdir_entry_del(fdir_info, fdir_entry);
+       sssnic_ethdev_fdir_entry_free(fdir_entry);
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_fdir_flow_rule_add(struct sssnic_ethdev_fdir_info *fdir_info,
+       struct sssnic_ethdev_fdir_rule *rule)
+{
+       struct sssnic_ethdev_fdir_entry *fdir_entry;
+       int ret;
+
+       fdir_entry = sssnic_ethdev_fdir_entry_alloc();
+       if (fdir_entry == NULL)
+               return -ENOMEM;
+
+       fdir_entry->rule = rule;
+
+       ret = sssnic_ethdev_tcam_entry_alloc(fdir_info->ethdev,
+               &fdir_entry->tcam_block, &fdir_entry->tcam_entry_idx);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to alloc TCAM entry");
+               goto tcam_entry_alloc_fail;
+       }
+
+       ret = sssnic_ethdev_fdir_entry_enable(fdir_info->ethdev, fdir_entry);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to enable fdir flow entry");
+               goto fdir_entry_enable_fail;
+       }
+
+       rule->cookie = fdir_entry;
+       sssnic_ethdev_fdir_entry_add(fdir_info, fdir_entry);
+
+       return 0;
+
+fdir_entry_enable_fail:
+       sssnic_ethdev_tcam_entry_free(fdir_info->ethdev, fdir_entry->tcam_block,
+               fdir_entry->tcam_entry_idx);
+tcam_entry_alloc_fail:
+       sssnic_ethdev_fdir_entry_free(fdir_entry);
+
+       return ret;
+}
+
+static int
+sssnic_ethdev_fdir_flow_rule_del(struct sssnic_ethdev_fdir_info *fdir_info,
+       struct sssnic_ethdev_fdir_rule *rule)
+{
+       struct sssnic_ethdev_fdir_entry *fdir_entry;
+       int ret;
+
+       fdir_entry = (struct sssnic_ethdev_fdir_entry *)rule->cookie;
+
+       ret = sssnic_ethdev_fdir_entry_disable(fdir_info->ethdev, fdir_entry);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to disable fdir flow entry");
+               return ret;
+       }
+
+       rule->cookie = NULL;
+       sssnic_ethdev_fdir_entry_del(fdir_info, fdir_entry);
+       sssnic_ethdev_fdir_entry_free(fdir_entry);
+
+       return 0;
+}
+
+int
+sssnic_ethdev_fdir_rule_add(struct rte_eth_dev *ethdev,
+       struct sssnic_ethdev_fdir_rule *rule)
+{
+       struct sssnic_ethdev_fdir_info *fdir_info;
+       int ret;
+
+       fdir_info = SSSNIC_ETHDEV_FDIR_INFO(ethdev);
+
+       if (sssnic_ethdev_fdir_entry_lookup(fdir_info, rule) != NULL) {
+               PMD_DRV_LOG(ERR, "FDIR rule exists!");
+               return -EEXIST;
+       }
+
+       if (rule->match.type == SSSNIC_ETHDEV_FDIR_MATCH_ETHERTYPE) {
+               ret = sssnic_ethdev_fdir_ethertype_rule_add(fdir_info, rule);
+               if (ret != 0) {
+                       PMD_DRV_LOG(ERR, "Failed to add fdir ethertype rule");
+                       return ret;
+               }
+               PMD_DRV_LOG(DEBUG,
+                       "Added fdir ethertype rule, total number of rules: %u",
+                       fdir_info->num_entries);
+       } else {
+               ret = sssnic_ethdev_fdir_flow_rule_add(fdir_info, rule);
+               if (ret != 0) {
+                       PMD_DRV_LOG(ERR, "Failed to add fdir flow rule");
+                       return ret;
+               }
+               PMD_DRV_LOG(DEBUG,
+                       "Added fdir flow rule, total number of rules: %u",
+                       fdir_info->num_entries);
+       }
+
+       ret = sssnic_ethdev_tcam_enable(ethdev);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to enable TCAM");
+               sssnic_ethdev_fdir_flow_rule_del(fdir_info, rule);
+       }
+
+       return ret;
+}
+
+int
+sssnic_ethdev_fdir_rule_del(struct rte_eth_dev *ethdev,
+       struct sssnic_ethdev_fdir_rule *fdir_rule)
+{
+       struct sssnic_ethdev_fdir_info *fdir_info;
+       struct sssnic_ethdev_fdir_entry *entry;
+       struct sssnic_ethdev_fdir_rule *rule;
+       int ret;
+
+       fdir_info = SSSNIC_ETHDEV_FDIR_INFO(ethdev);
+
+       entry = sssnic_ethdev_fdir_entry_lookup(fdir_info, fdir_rule);
+       if (entry == NULL)
+               return 0;
+
+       rule = entry->rule;
+       if (rule != fdir_rule)
+               return 0;
+
+       if (rule->match.type == SSSNIC_ETHDEV_FDIR_MATCH_ETHERTYPE) {
+               ret = sssnic_ethdev_fdir_ethertype_rule_del(fdir_info, rule);
+               if (ret != 0) {
+                       PMD_DRV_LOG(ERR,
+                               "Failed to delete fdir ethertype rule!");
+                       return ret;
+               }
+               PMD_DRV_LOG(DEBUG,
+                       "Deleted fdir ethertype rule, total number of rules: 
%u",
+                       fdir_info->num_entries);
+       } else {
+               ret = sssnic_ethdev_fdir_flow_rule_del(fdir_info, rule);
+               if (ret != 0) {
+                       PMD_DRV_LOG(ERR, "Failed to delete fdir flow rule!");
+                       return ret;
+               }
+               PMD_DRV_LOG(DEBUG,
+                       "Deleted fdir flow rule, total number of rules: %u",
+                       fdir_info->num_entries);
+       }
+
+       /* if there are no added rules, then disable TCAM */
+       if (fdir_info->num_entries == 0) {
+               ret = sssnic_ethdev_tcam_disable(ethdev);
+               if (ret != 0) {
+                       PMD_DRV_LOG(NOTICE,
+                               "There are no added rules, but failed to 
disable TCAM");
+                       ret = 0;
+               }
+       }
+
+       return ret;
+}
+
+int
+sssnic_ethdev_fdir_rules_disable_by_queue(struct rte_eth_dev *ethdev,
+       uint16_t qid)
+{
+       struct sssnic_ethdev_fdir_info *fdir_info;
+       struct sssnic_ethdev_fdir_entry *entry;
+       int ret;
+
+       fdir_info = SSSNIC_ETHDEV_FDIR_INFO(ethdev);
+
+       TAILQ_FOREACH(entry, &fdir_info->flow_entry_list, node)
+       {
+               if (entry->rule->action.qid == qid) {
+                       ret = sssnic_ethdev_fdir_entry_disable(ethdev, entry);
+                       if (ret != 0) {
+                               PMD_DRV_LOG(ERR,
+                                       "Failed to disable flow rule of 
queue:%u",
+                                       qid);
+
+                               return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+int
+sssnic_ethdev_fdir_rules_enable_by_queue(struct rte_eth_dev *ethdev,
+       uint16_t qid)
+{
+       struct sssnic_ethdev_fdir_info *fdir_info;
+       struct sssnic_ethdev_fdir_entry *entry;
+       int ret;
+
+       fdir_info = SSSNIC_ETHDEV_FDIR_INFO(ethdev);
+
+       TAILQ_FOREACH(entry, &fdir_info->flow_entry_list, node)
+       {
+               if (entry->rule->action.qid == qid) {
+                       ret = sssnic_ethdev_fdir_entry_enable(ethdev, entry);
+                       if (ret != 0) {
+                               PMD_DRV_LOG(ERR,
+                                       "Failed to enable flow rule of 
queue:%u",
+                                       qid);
+
+                               return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+int
+sssnic_ethdev_fdir_rules_flush(struct rte_eth_dev *ethdev)
+{
+       struct sssnic_netdev *netdev = SSSNIC_ETHDEV_PRIVATE(ethdev);
+       struct sssnic_ethdev_fdir_entry *entry, *tmp;
+       struct sssnic_ethdev_fdir_rule *rule;
+       int ret;
+
+       RTE_TAILQ_FOREACH_SAFE(entry, &netdev->fdir_info->flow_entry_list, node,
+               tmp)
+       {
+               rule = entry->rule;
+               ret = sssnic_ethdev_fdir_entry_disable(ethdev, entry);
+               if (ret != 0) {
+                       PMD_DRV_LOG(ERR, "Failed to disable fdir flow entry");
+                       return ret;
+               }
+               TAILQ_REMOVE(&netdev->fdir_info->flow_entry_list, entry, node);
+               sssnic_ethdev_fdir_entry_free(entry);
+               sssnic_ethdev_fdir_rule_free(rule);
+       }
+
+       RTE_TAILQ_FOREACH_SAFE(entry, &netdev->fdir_info->ethertype_entry_list,
+               node, tmp)
+       {
+               rule = entry->rule;
+               ret = sssnic_ethdev_fdir_entry_disable(ethdev, entry);
+               if (ret != 0) {
+                       PMD_DRV_LOG(ERR, "Failed to disable ethertype(%u) 
entry",
+                               rule->match.ethertype.key.ether_type);
+                       return ret;
+               }
+               TAILQ_REMOVE(&netdev->fdir_info->ethertype_entry_list, entry,
+                       node);
+               sssnic_ethdev_fdir_entry_free(entry);
+               sssnic_ethdev_fdir_rule_free(rule);
+       }
+
+       return 0;
+}
+
+int
+sssnic_ethdev_fdir_init(struct rte_eth_dev *ethdev)
+{
+       struct sssnic_netdev *netdev = SSSNIC_ETHDEV_PRIVATE(ethdev);
+
+       PMD_INIT_FUNC_TRACE();
+
+       netdev->fdir_info = rte_zmalloc("sssnic_fdir_info",
+               sizeof(struct sssnic_ethdev_fdir_info), 0);
+
+       if (netdev->fdir_info == NULL) {
+               PMD_DRV_LOG(ERR, "Failed to alloc fdir info memory for port %u",
+                       ethdev->data->port_id);
+               return -ENOMEM;
+       }
+
+       netdev->fdir_info->ethdev = ethdev;
+
+       TAILQ_INIT(&netdev->fdir_info->ethertype_entry_list);
+       TAILQ_INIT(&netdev->fdir_info->flow_entry_list);
+
+       sssnic_ethdev_tcam_init(ethdev);
+
+       return 0;
+}
+
+void
+sssnic_ethdev_fdir_shutdown(struct rte_eth_dev *ethdev)
+{
+       struct sssnic_netdev *netdev = SSSNIC_ETHDEV_PRIVATE(ethdev);
+       struct sssnic_ethdev_fdir_entry *entry, *tmp;
+
+       PMD_INIT_FUNC_TRACE();
+
+       if (netdev->fdir_info == NULL)
+               return;
+
+       RTE_TAILQ_FOREACH_SAFE(entry, &netdev->fdir_info->flow_entry_list, node,
+               tmp)
+       {
+               TAILQ_REMOVE(&netdev->fdir_info->flow_entry_list, entry, node);
+               sssnic_ethdev_fdir_entry_free(entry);
+       }
+
+       RTE_TAILQ_FOREACH_SAFE(entry, &netdev->fdir_info->ethertype_entry_list,
+               node, tmp)
+       {
+               TAILQ_REMOVE(&netdev->fdir_info->ethertype_entry_list, entry,
+                       node);
+               sssnic_ethdev_fdir_entry_free(entry);
+       }
+
+       sssnic_ethdev_tcam_shutdown(ethdev);
+
+       rte_free(netdev->fdir_info);
+}
diff --git a/drivers/net/sssnic/sssnic_ethdev_fdir.h 
b/drivers/net/sssnic/sssnic_ethdev_fdir.h
new file mode 100644
index 0000000000..aaf426b8f2
--- /dev/null
+++ b/drivers/net/sssnic/sssnic_ethdev_fdir.h
@@ -0,0 +1,332 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2022 Shenzhen 3SNIC Information Technology Co., Ltd.
+ */
+
+#ifndef _SSSNIC_ETHDEV_FDIR_H_
+#define _SSSNIC_ETHDEV_FDIR_H_
+
+#define SSSINC_ETHDEV_FDIR_FLOW_KEY_SIZE 44
+#define SSSNIC_ETHDEV_FDIR_FLOW_KEY_NUM_DW                                     
\
+       (SSSINC_ETHDEV_FDIR_FLOW_KEY_SIZE / sizeof(uint32_t))
+
+enum sssnic_ethdev_fdir_match_type {
+       SSSNIC_ETHDEV_FDIR_MATCH_ETHERTYPE = RTE_ETH_FILTER_ETHERTYPE,
+       SSSNIC_ETHDEV_FDIR_MATCH_FLOW = RTE_ETH_FILTER_FDIR,
+};
+
+enum sssnic_ethdev_fdir_flow_ip_type {
+       SSSNIC_ETHDEV_FDIR_FLOW_IPV4 = 0,
+       SSSNIC_ETHDEV_FDIR_FLOW_IPV6 = 1,
+};
+
+enum sssnic_ethdev_fdir_flow_tunnel_type {
+       SSSNIC_ETHDEV_FDIR_FLOW_TUNNEL_NONE = 0,
+       SSSNIC_ETHDEV_FDIR_FLOW_TUNNEL_VXLAN = 1,
+};
+
+#define SSSNIC_ETHDEV_FDIR_FLOW_FUNC_ID_MASK 0x7fff
+#define SSSNIC_ETHDEV_FDIR_FLOW_IP_TYPE_MASK 0x1
+#define SSSNIC_ETHDEV_FDIR_FLOW_TUNNEL_TYPE_MASK 0xf
+
+struct sssnic_ethdev_fdir_ethertype_key {
+       uint16_t ether_type;
+};
+
+struct sssnic_ethdev_fdir_ipv4_flow_key {
+#if (RTE_BYTE_ORDER == RTE_BIG_ENDIAN)
+       uint32_t resvd0 : 16;
+       uint32_t ip_proto : 8;
+       uint32_t tunnel_type : 4;
+       uint32_t resvd1 : 4;
+
+       uint32_t func_id : 15;
+       uint32_t ip_type : 1;
+       uint32_t sip_w1 : 16;
+
+       uint32_t sip_w0 : 16;
+       uint32_t dip_w1 : 16;
+
+       uint32_t dip_w0 : 16;
+       uint32_t resvd2 : 16;
+
+       uint32_t resvd3;
+
+       uint32_t resvd4 : 16;
+       uint32_t dport : 16;
+
+       uint32_t sport : 16;
+       uint32_t resvd5 : 16;
+
+       uint32_t resvd6 : 16;
+       uint32_t outer_sip_w1 : 16;
+
+       uint32_t outer_sip_w0 : 16;
+       uint32_t outer_dip_w1 : 16;
+
+       uint32_t outer_dip_w0 : 16;
+       uint32_t vni_w1 : 16;
+
+       uint32_t vni_w0 : 16;
+       uint32_t resvd7 : 16;
+#else
+       uint32_t resvd1 : 4;
+       uint32_t tunnel_type : 4;
+       uint32_t ip_proto : 8;
+       uint32_t resvd0 : 16;
+
+       uint32_t sip_w1 : 16;
+       uint32_t ip_type : 1;
+       uint32_t func_id : 15;
+
+       uint32_t dip_w1 : 16;
+       uint32_t sip_w0 : 16;
+
+       uint32_t resvd2 : 16;
+       uint32_t dip_w0 : 16;
+
+       uint32_t rsvd3;
+
+       uint32_t dport : 16;
+       uint32_t resvd4 : 16;
+
+       uint32_t resvd5 : 16;
+       uint32_t sport : 16;
+
+       uint32_t outer_sip_w1 : 16;
+       uint32_t resvd6 : 16;
+
+       uint32_t outer_dip_w1 : 16;
+       uint32_t outer_sip_w0 : 16;
+
+       uint32_t vni_w1 : 16;
+       uint32_t outer_dip_w0 : 16;
+
+       uint32_t resvd7 : 16;
+       uint32_t vni_w0 : 16;
+#endif
+};
+
+struct sssnic_ethdev_fdir_ipv6_flow_key {
+#if (RTE_BYTE_ORDER == RTE_BIG_ENDIAN)
+       uint32_t resvd0 : 16;
+       uint32_t ip_proto : 8;
+       uint32_t tunnel_type : 4;
+       uint32_t resvd1 : 4;
+
+       uint32_t func_id : 15;
+       uint32_t ip_type : 1;
+       uint32_t sip6_w0 : 16;
+
+       uint32_t sip6_w1 : 16;
+       uint32_t sip6_w2 : 16;
+
+       uint32_t sip6_w3 : 16;
+       uint32_t sip6_w4 : 16;
+
+       uint32_t sip6_w5 : 16;
+       uint32_t sip6_w6 : 16;
+
+       uint32_t sip6_w7 : 16;
+       uint32_t dport : 16;
+
+       uint32_t sport : 16;
+       uint32_t dip6_w0 : 16;
+
+       uint32_t dip6_w1 : 16;
+       uint32_t dip6_w2 : 16;
+
+       uint32_t dip6_w3 : 16;
+       uint32_t dip6_w4 : 16;
+
+       uint32_t dip6_w5 : 16;
+       uint32_t dip6_w6 : 16;
+
+       uint32_t dip6_w7 : 16;
+       uint32_t resvd2 : 16;
+#else
+       uint32_t resvd1 : 4;
+       uint32_t tunnel_type : 4;
+       uint32_t ip_proto : 8;
+       uint32_t resvd0 : 16;
+
+       uint32_t sip6_w0 : 16;
+       uint32_t ip_type : 1;
+       uint32_t func_id : 15;
+
+       uint32_t sip6_w2 : 16;
+       uint32_t sip6_w1 : 16;
+
+       uint32_t sip6_w4 : 16;
+       uint32_t sip6_w3 : 16;
+
+       uint32_t sip6_w6 : 16;
+       uint32_t sip6_w5 : 16;
+
+       uint32_t dport : 16;
+       uint32_t sip6_w7 : 16;
+
+       uint32_t dip6_w0 : 16;
+       uint32_t sport : 16;
+
+       uint32_t dip6_w2 : 16;
+       uint32_t dip6_w1 : 16;
+
+       uint32_t dip6_w4 : 16;
+       uint32_t dip6_w3 : 16;
+
+       uint32_t dip6_w6 : 16;
+       uint32_t dip6_w5 : 16;
+
+       uint32_t resvd2 : 16;
+       uint32_t dip6_w7 : 16;
+#endif
+};
+
+struct sssnic_ethdev_fdir_vxlan_ipv6_flow_key {
+#if (RTE_BYTE_ORDER == RTE_BIG_ENDIAN)
+       uint32_t resvd0 : 16;
+       uint32_t ip_proto : 8;
+       uint32_t tunnel_type : 4;
+       uint32_t resvd1 : 4;
+
+       uint32_t func_id : 15;
+       uint32_t ip_type : 1;
+       uint32_t dip6_w0 : 16;
+
+       uint32_t dip6_w1 : 16;
+       uint32_t dip6_w2 : 16;
+
+       uint32_t dip6_w3 : 16;
+       uint32_t dip6_w4 : 16;
+
+       uint32_t dip6_w5 : 16;
+       uint32_t dip6_w6 : 16;
+
+       uint32_t dip6_w7 : 16;
+       uint32_t dport : 16;
+
+       uint32_t sport : 16;
+       uint32_t resvd2 : 16;
+
+       uint32_t resvd3 : 16;
+       uint32_t outer_sip_w1 : 16;
+
+       uint32_t outer_sip_w0 : 16;
+       uint32_t outer_dip_w1 : 16;
+
+       uint32_t outer_dip_w0 : 16;
+       uint32_t vni_w1 : 16;
+
+       uint32_t vni_w0 : 16;
+       uint32_t resvd4 : 16;
+#else
+       uint32_t rsvd1 : 4;
+       uint32_t tunnel_type : 4;
+       uint32_t ip_proto : 8;
+       uint32_t resvd0 : 16;
+
+       uint32_t dip6_w0 : 16;
+       uint32_t ip_type : 1;
+       uint32_t function_id : 15;
+
+       uint32_t dip6_w2 : 16;
+       uint32_t dip6_w1 : 16;
+
+       uint32_t dip6_w4 : 16;
+       uint32_t dip6_w3 : 16;
+
+       uint32_t dip6_w6 : 16;
+       uint32_t dip6_w5 : 16;
+
+       uint32_t dport : 16;
+       uint32_t dip6_w7 : 16;
+
+       uint32_t resvd2 : 16;
+       uint32_t sport : 16;
+
+       uint32_t outer_sip_w1 : 16;
+       uint32_t resvd3 : 16;
+
+       uint32_t outer_dip_w1 : 16;
+       uint32_t outer_sip_w0 : 16;
+
+       uint32_t vni_w1 : 16;
+       uint32_t outer_dip_w0 : 16;
+
+       uint32_t resvd4 : 16;
+       uint32_t vni_w0 : 16;
+#endif
+};
+
+struct sssnic_ethdev_fdir_flow_key {
+       union {
+               uint32_t dword[SSSNIC_ETHDEV_FDIR_FLOW_KEY_NUM_DW];
+               struct {
+                       struct sssnic_ethdev_fdir_ipv4_flow_key ipv4;
+                       struct sssnic_ethdev_fdir_ipv6_flow_key ipv6;
+                       struct sssnic_ethdev_fdir_vxlan_ipv6_flow_key 
vxlan_ipv6;
+               };
+       };
+};
+
+struct sssnic_ethdev_fdir_flow_match {
+       struct sssnic_ethdev_fdir_flow_key key;
+       struct sssnic_ethdev_fdir_flow_key mask;
+};
+
+struct sssnic_ethdev_fdir_ethertype_match {
+       struct sssnic_ethdev_fdir_ethertype_key key;
+};
+
+struct sssnic_ethdev_fdir_match {
+       enum sssnic_ethdev_fdir_match_type type;
+       union {
+               struct sssnic_ethdev_fdir_flow_match flow;
+               struct sssnic_ethdev_fdir_ethertype_match ethertype;
+       };
+};
+
+struct sssnic_ethdev_fdir_action {
+       uint16_t qid;
+};
+
+/* struct sssnic_ethdev_fdir_rule must be dynamically allocated in the heap */
+struct sssnic_ethdev_fdir_rule {
+       struct sssnic_ethdev_fdir_match match;
+       struct sssnic_ethdev_fdir_action action;
+       void *cookie; /* low level data, initial value must be set to  NULL*/
+};
+
+struct sssnic_ethdev_fdir_info;
+
+static inline struct sssnic_ethdev_fdir_rule *
+sssnic_ethdev_fdir_rule_alloc(void)
+{
+       struct sssnic_ethdev_fdir_rule *rule;
+
+       rule = rte_zmalloc("sssnic_fdir_rule",
+               sizeof(struct sssnic_ethdev_fdir_rule), 0);
+
+       return rule;
+}
+
+static inline void
+sssnic_ethdev_fdir_rule_free(struct sssnic_ethdev_fdir_rule *rule)
+{
+       if (rule != NULL)
+               rte_free(rule);
+}
+
+int sssnic_ethdev_fdir_rules_disable_by_queue(struct rte_eth_dev *ethdev,
+       uint16_t qid);
+int sssnic_ethdev_fdir_rules_enable_by_queue(struct rte_eth_dev *ethdev,
+       uint16_t qid);
+int sssnic_ethdev_fdir_rule_add(struct rte_eth_dev *ethdev,
+       struct sssnic_ethdev_fdir_rule *rule);
+int sssnic_ethdev_fdir_rule_del(struct rte_eth_dev *ethdev,
+       struct sssnic_ethdev_fdir_rule *fdir_rule);
+int sssnic_ethdev_fdir_rules_flush(struct rte_eth_dev *ethdev);
+int sssnic_ethdev_fdir_init(struct rte_eth_dev *ethdev);
+void sssnic_ethdev_fdir_shutdown(struct rte_eth_dev *ethdev);
+
+#endif /* _SSSNIC_ETHDEV_FDIR_H_ */
diff --git a/drivers/net/sssnic/sssnic_ethdev_flow.c 
b/drivers/net/sssnic/sssnic_ethdev_flow.c
new file mode 100644
index 0000000000..372a5bed6b
--- /dev/null
+++ b/drivers/net/sssnic/sssnic_ethdev_flow.c
@@ -0,0 +1,981 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2022 Shenzhen 3SNIC Information Technology Co., Ltd.
+ */
+
+#include <rte_common.h>
+#include <ethdev_pci.h>
+#include <rte_flow_driver.h>
+
+#include "sssnic_log.h"
+#include "sssnic_ethdev.h"
+#include "sssnic_ethdev_fdir.h"
+#include "sssnic_ethdev_flow.h"
+#include "base/sssnic_hw.h"
+#include "base/sssnic_api.h"
+#include "base/sssnic_misc.h"
+
+struct rte_flow {
+       struct sssnic_ethdev_fdir_rule rule;
+};
+
+static enum rte_flow_item_type pattern_ethertype[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+static enum rte_flow_item_type pattern_eth_ipv4[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV4,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+static enum rte_flow_item_type pattern_eth_ipv4_udp[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV4,
+       RTE_FLOW_ITEM_TYPE_UDP,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+static enum rte_flow_item_type pattern_eth_ipv4_tcp[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV4,
+       RTE_FLOW_ITEM_TYPE_TCP,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+static enum rte_flow_item_type pattern_eth_ipv4_any[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV4,
+       RTE_FLOW_ITEM_TYPE_ANY,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV4,
+       RTE_FLOW_ITEM_TYPE_UDP,
+       RTE_FLOW_ITEM_TYPE_VXLAN,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_eth_ipv4[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV4,
+       RTE_FLOW_ITEM_TYPE_UDP,
+       RTE_FLOW_ITEM_TYPE_VXLAN,
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV4,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_udp[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV4,
+       RTE_FLOW_ITEM_TYPE_UDP,
+       RTE_FLOW_ITEM_TYPE_VXLAN,
+       RTE_FLOW_ITEM_TYPE_UDP,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_tcp[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV4,
+       RTE_FLOW_ITEM_TYPE_UDP,
+       RTE_FLOW_ITEM_TYPE_VXLAN,
+       RTE_FLOW_ITEM_TYPE_TCP,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_any[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV4,
+       RTE_FLOW_ITEM_TYPE_UDP,
+       RTE_FLOW_ITEM_TYPE_VXLAN,
+       RTE_FLOW_ITEM_TYPE_ANY,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_eth_ipv4_tcp[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV4,
+       RTE_FLOW_ITEM_TYPE_UDP,
+       RTE_FLOW_ITEM_TYPE_VXLAN,
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV4,
+       RTE_FLOW_ITEM_TYPE_TCP,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_eth_ipv4_udp[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV4,
+       RTE_FLOW_ITEM_TYPE_UDP,
+       RTE_FLOW_ITEM_TYPE_VXLAN,
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV4,
+       RTE_FLOW_ITEM_TYPE_UDP,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_eth_ipv6[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV4,
+       RTE_FLOW_ITEM_TYPE_UDP,
+       RTE_FLOW_ITEM_TYPE_VXLAN,
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV6,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_eth_ipv6_tcp[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV4,
+       RTE_FLOW_ITEM_TYPE_UDP,
+       RTE_FLOW_ITEM_TYPE_VXLAN,
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV6,
+       RTE_FLOW_ITEM_TYPE_TCP,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_eth_ipv6_udp[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV4,
+       RTE_FLOW_ITEM_TYPE_UDP,
+       RTE_FLOW_ITEM_TYPE_VXLAN,
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV6,
+       RTE_FLOW_ITEM_TYPE_UDP,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+static enum rte_flow_item_type pattern_eth_ipv6[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV6,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+static enum rte_flow_item_type pattern_eth_ipv6_udp[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV6,
+       RTE_FLOW_ITEM_TYPE_UDP,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+static enum rte_flow_item_type pattern_eth_ipv6_tcp[] = {
+       RTE_FLOW_ITEM_TYPE_ETH,
+       RTE_FLOW_ITEM_TYPE_IPV6,
+       RTE_FLOW_ITEM_TYPE_TCP,
+       RTE_FLOW_ITEM_TYPE_END,
+};
+
+enum sssnic_ethdev_flow_type {
+       SSSNIC_ETHDEV_FLOW_TYPE_UNKNOWN = -1,
+       SSSNIC_ETHDEV_FLOW_TYPE_ETHERTYPE,
+       SSSNIC_ETHDEV_FLOW_TYPE_FDIR,
+       SSSNIC_ETHDEV_FLOW_TYPE_COUNT,
+};
+
+struct sssnic_ethdev_flow_pattern {
+       enum rte_flow_item_type *flow_items;
+       enum sssnic_ethdev_flow_type type;
+       bool is_tunnel;
+};
+
+static struct sssnic_ethdev_flow_pattern supported_flow_patterns[] = {
+       { pattern_ethertype, SSSNIC_ETHDEV_FLOW_TYPE_ETHERTYPE, false },
+       { pattern_eth_ipv4, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, false },
+       { pattern_eth_ipv4_udp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, false },
+       { pattern_eth_ipv4_tcp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, false },
+       { pattern_eth_ipv4_any, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, false },
+       { pattern_eth_ipv4_udp_vxlan, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, true },
+       { pattern_eth_ipv4_udp_vxlan_udp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, true },
+       { pattern_eth_ipv4_udp_vxlan_tcp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, true },
+       { pattern_eth_ipv4_udp_vxlan_any, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, true },
+       { pattern_eth_ipv4_udp_vxlan_eth_ipv4, SSSNIC_ETHDEV_FLOW_TYPE_FDIR,
+               true },
+       { pattern_eth_ipv4_udp_vxlan_eth_ipv4_tcp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR,
+               true },
+       { pattern_eth_ipv4_udp_vxlan_eth_ipv4_udp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR,
+               true },
+       { pattern_eth_ipv4_udp_vxlan_eth_ipv6, SSSNIC_ETHDEV_FLOW_TYPE_FDIR,
+               true },
+       { pattern_eth_ipv4_udp_vxlan_eth_ipv6_tcp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR,
+               true },
+       { pattern_eth_ipv4_udp_vxlan_eth_ipv6_udp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR,
+               true },
+       { pattern_eth_ipv6, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, false },
+       { pattern_eth_ipv6_udp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, false },
+       { pattern_eth_ipv6_tcp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, false },
+};
+
+static bool
+sssnic_ethdev_flow_pattern_match(enum rte_flow_item_type *item_array,
+       const struct rte_flow_item *pattern)
+{
+       const struct rte_flow_item *item = pattern;
+
+       /* skip void items in the head of pattern */
+       while (item->type == RTE_FLOW_ITEM_TYPE_VOID)
+               item++;
+
+       while ((*item_array == item->type) &&
+               (*item_array != RTE_FLOW_ITEM_TYPE_END)) {
+               item_array++;
+               item++;
+       }
+
+       return (*item_array == RTE_FLOW_ITEM_TYPE_END &&
+               item->type == RTE_FLOW_ITEM_TYPE_END);
+}
+
+static struct sssnic_ethdev_flow_pattern *
+sssnic_ethdev_flow_pattern_lookup(const struct rte_flow_item *pattern)
+{
+       struct sssnic_ethdev_flow_pattern *flow_pattern;
+       enum rte_flow_item_type *flow_items;
+       size_t i;
+
+       for (i = 0; i < RTE_DIM(supported_flow_patterns); i++) {
+               flow_pattern = &supported_flow_patterns[i];
+               flow_items = flow_pattern->flow_items;
+               if (sssnic_ethdev_flow_pattern_match(flow_items, pattern))
+                       return flow_pattern;
+       }
+
+       return NULL;
+}
+
+static int
+sssnic_ethdev_flow_action_parse(struct rte_eth_dev *ethdev,
+       const struct rte_flow_action *actions, struct rte_flow_error *error,
+       struct sssnic_ethdev_fdir_rule *fdir_rule)
+{
+       const struct rte_flow_action_queue *action_queue;
+       const struct rte_flow_action *action = actions;
+
+       if (action->type != RTE_FLOW_ACTION_TYPE_QUEUE) {
+               rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
+                       NULL,
+                       "Unsupported action type, only support action queue");
+               return -EINVAL;
+       }
+
+       action_queue = (const struct rte_flow_action_queue *)action->conf;
+       if (action_queue->index >= ethdev->data->nb_rx_queues) {
+               rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
+                       NULL, "Invalid queue index");
+               return -EINVAL;
+       }
+
+       if (fdir_rule != NULL)
+               fdir_rule->action.qid = action_queue->index;
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_flow_ethertype_pattern_parse(const struct rte_flow_item *pattern,
+       struct rte_flow_error *error, struct sssnic_ethdev_fdir_rule *fdir_rule)
+{
+       const struct rte_flow_item *item = pattern;
+       const struct rte_flow_item_eth *spec, *mask;
+       struct sssnic_ethdev_fdir_ethertype_match *fdir_match;
+
+       while (item->type != RTE_FLOW_ITEM_TYPE_ETH)
+               item++;
+
+       spec = (const struct rte_flow_item_eth *)item->spec;
+       mask = (const struct rte_flow_item_eth *)item->mask;
+
+       if (item->last != NULL) {
+               rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_LAST,
+                       item, "Not support range");
+               return -rte_errno;
+       }
+
+       if (spec == NULL || mask == NULL) {
+               rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_LAST,
+                       item, "Ether mask or spec is NULL");
+               return -rte_errno;
+       }
+
+       if (!rte_is_zero_ether_addr(&mask->src) ||
+               !rte_is_zero_ether_addr(&mask->dst)) {
+               rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
+                       item, "Invalid ether address mask");
+               return -rte_errno;
+       }
+
+       if (mask->type != 0xffff) {
+               rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK,
+                       item, "Invalid ether type mask");
+               return -rte_errno;
+       }
+
+       if (fdir_rule != NULL) {
+               fdir_rule->match.type = SSSNIC_ETHDEV_FDIR_MATCH_ETHERTYPE;
+               fdir_match = &fdir_rule->match.ethertype;
+               fdir_match->key.ether_type = rte_be_to_cpu_16(spec->type);
+       }
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_flow_eth_parse(const struct rte_flow_item *item,
+       struct rte_flow_error *error)
+{
+       if (item->spec != NULL || item->mask != NULL) {
+               rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
+                       item, "Not support eth match in fdir flow");
+               return -rte_errno;
+       }
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_flow_ipv4_parse(const struct rte_flow_item *item,
+       struct rte_flow_error *error, bool outer,
+       struct sssnic_ethdev_fdir_flow_match *fdir_match)
+{
+       const struct rte_flow_item_ipv4 *spec, *mask;
+       uint32_t ip_addr;
+
+       spec = (const struct rte_flow_item_ipv4 *)item->spec;
+       mask = (const struct rte_flow_item_ipv4 *)item->mask;
+
+       if (outer) {
+               /* only tunnel flow has outer ipv4 */
+               if (spec == NULL && mask == NULL)
+                       return 0;
+
+               if (spec == NULL || mask == NULL) {
+                       rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ITEM, item,
+                               "Invalid IPV4 spec or mask");
+                       return -rte_errno;
+               }
+
+               if (mask->hdr.version_ihl || mask->hdr.type_of_service ||
+                       mask->hdr.total_length || mask->hdr.packet_id ||
+                       mask->hdr.fragment_offset || mask->hdr.time_to_live ||
+                       mask->hdr.next_proto_id || mask->hdr.hdr_checksum) {
+                       rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ITEM, item,
+                               "Only support outer IPv4 src and dest address 
for tunnel flow");
+                       return -rte_errno;
+               }
+
+               if (fdir_match != NULL) {
+                       ip_addr = rte_be_to_cpu_32(spec->hdr.src_addr);
+                       fdir_match->key.ipv4.outer_sip_w0 = (uint16_t)ip_addr;
+                       fdir_match->key.ipv4.outer_sip_w1 =
+                               (uint16_t)(ip_addr >> 16);
+
+                       ip_addr = rte_be_to_cpu_32(mask->hdr.src_addr);
+                       fdir_match->mask.ipv4.outer_sip_w0 = (uint16_t)ip_addr;
+                       fdir_match->mask.ipv4.outer_sip_w1 =
+                               (uint16_t)(ip_addr >> 16);
+               }
+       } else {
+               /* inner ip of tunnel flow or ip of non tunnel flow */
+               if (spec == NULL && mask == NULL)
+                       return 0;
+
+               if (spec == NULL || mask == NULL) {
+                       rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ITEM, item,
+                               "Invalid IPV4 spec or mask");
+                       return -rte_errno;
+               }
+
+               if (mask->hdr.version_ihl || mask->hdr.type_of_service ||
+                       mask->hdr.total_length || mask->hdr.packet_id ||
+                       mask->hdr.fragment_offset || mask->hdr.time_to_live ||
+                       mask->hdr.hdr_checksum) {
+                       rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ITEM, item,
+                               "Only support IPv4 address and ipproto");
+                       return -rte_errno;
+               }
+
+               if (fdir_match != NULL) {
+                       ip_addr = rte_be_to_cpu_32(spec->hdr.src_addr);
+                       fdir_match->key.ipv4.sip_w0 = (uint16_t)ip_addr;
+                       fdir_match->key.ipv4.sip_w1 = (uint16_t)(ip_addr >> 16);
+
+                       ip_addr = rte_be_to_cpu_32(mask->hdr.src_addr);
+                       fdir_match->mask.ipv4.sip_w0 = (uint16_t)ip_addr;
+                       fdir_match->mask.ipv4.sip_w1 =
+                               (uint16_t)(ip_addr >> 16);
+
+                       fdir_match->key.ipv4.ip_proto = spec->hdr.next_proto_id;
+                       fdir_match->mask.ipv4.ip_proto =
+                               mask->hdr.next_proto_id;
+
+                       fdir_match->key.ipv4.ip_type =
+                               SSSNIC_ETHDEV_FDIR_FLOW_IPV4;
+                       fdir_match->mask.ipv4.ip_type = 0x1;
+               }
+       }
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_flow_ipv6_parse(const struct rte_flow_item *item,
+       struct rte_flow_error *error, bool is_tunnel,
+       struct sssnic_ethdev_fdir_flow_match *fdir_match)
+{
+       const struct rte_flow_item_ipv6 *spec, *mask;
+       uint32_t ipv6_addr[4];
+       int i;
+
+       mask = (const struct rte_flow_item_ipv6 *)item->mask;
+       spec = (const struct rte_flow_item_ipv6 *)item->spec;
+
+       if (fdir_match != NULL) {
+               /* ip_type of ipv6 flow_match can share with other flow_matches 
*/
+               fdir_match->key.ipv6.ip_type = SSSNIC_ETHDEV_FDIR_FLOW_IPV6;
+               fdir_match->mask.ipv6.ip_type = 0x1;
+       }
+
+       if (is_tunnel) {
+               if (mask == NULL && spec == NULL)
+                       return 0;
+
+               if (spec == NULL || mask == NULL) {
+                       rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ITEM, item,
+                               "Invalid IPV6 spec or mask");
+                       return -rte_errno;
+               }
+
+               if (mask->hdr.vtc_flow || mask->hdr.payload_len ||
+                       mask->hdr.hop_limits ||
+                       !sssnic_is_zero_ipv6_addr(mask->hdr.src_addr)) {
+                       rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ITEM, item,
+                               "Only support IPv6 dest_addr and ipproto in 
tunnel flow");
+                       return -rte_errno;
+               }
+
+               if (fdir_match != NULL) {
+                       rte_memcpy(ipv6_addr, spec->hdr.dst_addr,
+                               sizeof(ipv6_addr));
+                       for (i = 0; i < 4; i++)
+                               ipv6_addr[i] = rte_be_to_cpu_32(ipv6_addr[i]);
+
+                       fdir_match->key.vxlan_ipv6.dip6_w0 =
+                               (uint16_t)ipv6_addr[0];
+                       fdir_match->key.vxlan_ipv6.dip6_w1 =
+                               (uint16_t)(ipv6_addr[0] >> 16);
+                       fdir_match->key.vxlan_ipv6.dip6_w2 =
+                               (uint16_t)ipv6_addr[1];
+                       fdir_match->key.vxlan_ipv6.dip6_w3 =
+                               (uint16_t)(ipv6_addr[1] >> 16);
+                       fdir_match->key.vxlan_ipv6.dip6_w4 =
+                               (uint16_t)ipv6_addr[2];
+                       fdir_match->key.vxlan_ipv6.dip6_w5 =
+                               (uint16_t)(ipv6_addr[2] >> 16);
+                       fdir_match->key.vxlan_ipv6.dip6_w6 =
+                               (uint16_t)ipv6_addr[3];
+                       fdir_match->key.vxlan_ipv6.dip6_w7 =
+                               (uint16_t)(ipv6_addr[3] >> 16);
+
+                       rte_memcpy(ipv6_addr, mask->hdr.dst_addr,
+                               sizeof(ipv6_addr));
+                       for (i = 0; i < 4; i++)
+                               ipv6_addr[i] = rte_be_to_cpu_32(ipv6_addr[i]);
+
+                       fdir_match->mask.vxlan_ipv6.dip6_w0 =
+                               (uint16_t)ipv6_addr[0];
+                       fdir_match->mask.vxlan_ipv6.dip6_w1 =
+                               (uint16_t)(ipv6_addr[0] >> 16);
+                       fdir_match->mask.vxlan_ipv6.dip6_w2 =
+                               (uint16_t)ipv6_addr[1];
+                       fdir_match->mask.vxlan_ipv6.dip6_w3 =
+                               (uint16_t)(ipv6_addr[1] >> 16);
+                       fdir_match->mask.vxlan_ipv6.dip6_w4 =
+                               (uint16_t)ipv6_addr[2];
+                       fdir_match->mask.vxlan_ipv6.dip6_w5 =
+                               (uint16_t)(ipv6_addr[2] >> 16);
+                       fdir_match->mask.vxlan_ipv6.dip6_w6 =
+                               (uint16_t)ipv6_addr[3];
+                       fdir_match->mask.vxlan_ipv6.dip6_w7 =
+                               (uint16_t)(ipv6_addr[3] >> 16);
+
+                       fdir_match->key.vxlan_ipv6.ip_proto = spec->hdr.proto;
+                       fdir_match->mask.vxlan_ipv6.ip_proto = mask->hdr.proto;
+               }
+       } else { /* non tunnel */
+               if (spec == NULL || mask == NULL) {
+                       rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ITEM, item,
+                               "Invalid IPV6 spec or mask");
+                       return -rte_errno;
+               }
+
+               if (mask->hdr.vtc_flow || mask->hdr.payload_len ||
+                       mask->hdr.hop_limits) {
+                       rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ITEM, item,
+                               "Only support IPv6 addr and ipproto");
+                       return -rte_errno;
+               }
+
+               if (fdir_match != NULL) {
+                       rte_memcpy(ipv6_addr, spec->hdr.dst_addr,
+                               sizeof(ipv6_addr));
+                       for (i = 0; i < 4; i++)
+                               ipv6_addr[i] = rte_be_to_cpu_32(ipv6_addr[i]);
+
+                       fdir_match->key.ipv6.dip6_w0 = (uint16_t)ipv6_addr[0];
+                       fdir_match->key.ipv6.dip6_w1 =
+                               (uint16_t)(ipv6_addr[0] >> 16);
+                       fdir_match->key.ipv6.dip6_w2 = (uint16_t)ipv6_addr[1];
+                       fdir_match->key.ipv6.dip6_w3 =
+                               (uint16_t)(ipv6_addr[1] >> 16);
+                       fdir_match->key.ipv6.dip6_w4 = (uint16_t)ipv6_addr[2];
+                       fdir_match->key.ipv6.dip6_w5 =
+                               (uint16_t)(ipv6_addr[2] >> 16);
+                       fdir_match->key.ipv6.dip6_w6 = (uint16_t)ipv6_addr[3];
+                       fdir_match->key.ipv6.dip6_w7 =
+                               (uint16_t)(ipv6_addr[3] >> 16);
+
+                       rte_memcpy(ipv6_addr, spec->hdr.src_addr,
+                               sizeof(ipv6_addr));
+                       for (i = 0; i < 4; i++)
+                               ipv6_addr[i] = rte_be_to_cpu_32(ipv6_addr[i]);
+
+                       fdir_match->key.ipv6.sip6_w0 = (uint16_t)ipv6_addr[0];
+                       fdir_match->key.ipv6.sip6_w1 =
+                               (uint16_t)(ipv6_addr[0] >> 16);
+                       fdir_match->key.ipv6.sip6_w2 = (uint16_t)ipv6_addr[1];
+                       fdir_match->key.ipv6.sip6_w3 =
+                               (uint16_t)(ipv6_addr[1] >> 16);
+                       fdir_match->key.ipv6.sip6_w4 = (uint16_t)ipv6_addr[2];
+                       fdir_match->key.ipv6.sip6_w5 =
+                               (uint16_t)(ipv6_addr[2] >> 16);
+                       fdir_match->key.ipv6.sip6_w6 = (uint16_t)ipv6_addr[3];
+                       fdir_match->key.ipv6.sip6_w7 =
+                               (uint16_t)(ipv6_addr[3] >> 16);
+
+                       rte_memcpy(ipv6_addr, mask->hdr.dst_addr,
+                               sizeof(ipv6_addr));
+                       for (i = 0; i < 4; i++)
+                               ipv6_addr[i] = rte_be_to_cpu_32(ipv6_addr[i]);
+
+                       fdir_match->mask.ipv6.dip6_w0 = (uint16_t)ipv6_addr[0];
+                       fdir_match->mask.ipv6.dip6_w1 =
+                               (uint16_t)(ipv6_addr[0] >> 16);
+                       fdir_match->mask.ipv6.dip6_w2 = (uint16_t)ipv6_addr[1];
+                       fdir_match->mask.ipv6.dip6_w3 =
+                               (uint16_t)(ipv6_addr[1] >> 16);
+                       fdir_match->mask.ipv6.dip6_w4 = (uint16_t)ipv6_addr[2];
+                       fdir_match->mask.ipv6.dip6_w5 =
+                               (uint16_t)(ipv6_addr[2] >> 16);
+                       fdir_match->mask.ipv6.dip6_w6 = (uint16_t)ipv6_addr[3];
+                       fdir_match->mask.ipv6.dip6_w7 =
+                               (uint16_t)(ipv6_addr[3] >> 16);
+
+                       rte_memcpy(ipv6_addr, mask->hdr.src_addr,
+                               sizeof(ipv6_addr));
+                       for (i = 0; i < 4; i++)
+                               ipv6_addr[i] = rte_be_to_cpu_32(ipv6_addr[i]);
+
+                       fdir_match->mask.ipv6.sip6_w0 = (uint16_t)ipv6_addr[0];
+                       fdir_match->mask.ipv6.sip6_w1 =
+                               (uint16_t)(ipv6_addr[0] >> 16);
+                       fdir_match->mask.ipv6.sip6_w2 = (uint16_t)ipv6_addr[1];
+                       fdir_match->mask.ipv6.sip6_w3 =
+                               (uint16_t)(ipv6_addr[1] >> 16);
+                       fdir_match->mask.ipv6.sip6_w4 = (uint16_t)ipv6_addr[2];
+                       fdir_match->mask.ipv6.sip6_w5 =
+                               (uint16_t)(ipv6_addr[2] >> 16);
+                       fdir_match->mask.ipv6.sip6_w6 = (uint16_t)ipv6_addr[3];
+                       fdir_match->mask.ipv6.sip6_w7 =
+                               (uint16_t)(ipv6_addr[3] >> 16);
+
+                       fdir_match->key.ipv6.ip_proto = spec->hdr.proto;
+                       fdir_match->mask.ipv6.ip_proto = mask->hdr.proto;
+               }
+       }
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_flow_udp_parse(const struct rte_flow_item *item,
+       struct rte_flow_error *error, bool outer,
+       struct sssnic_ethdev_fdir_flow_match *fdir_match)
+{
+       const struct rte_flow_item_udp *spec, *mask;
+
+       spec = (const struct rte_flow_item_udp *)item->spec;
+       mask = (const struct rte_flow_item_udp *)item->mask;
+
+       if (outer) {
+               if (spec != NULL || mask != NULL) {
+                       rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ITEM, item,
+                               "Both of outer UDP spec and mask must be NULL 
in tunnel flow");
+                       return -rte_errno;
+               }
+
+               return 0;
+       }
+
+       if (fdir_match != NULL) {
+               /* ipv6 match can share ip_proto with ipv4 match */
+               fdir_match->key.ipv4.ip_proto = IPPROTO_UDP;
+               fdir_match->mask.ipv4.ip_proto = 0xff;
+       }
+
+       if (spec == NULL && mask == NULL)
+               return 0;
+
+       if (spec == NULL || mask == NULL) {
+               rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
+                       item, "Invalid UDP spec or mask");
+               return -rte_errno;
+       }
+
+       if (fdir_match != NULL) {
+               /* Other types of fdir match can share sport and dport with 
ipv4 match */
+               fdir_match->key.ipv4.sport =
+                       rte_be_to_cpu_16(spec->hdr.src_port);
+               fdir_match->mask.ipv4.sport =
+                       rte_be_to_cpu_16(mask->hdr.src_port);
+               fdir_match->key.ipv4.dport =
+                       rte_be_to_cpu_16(spec->hdr.dst_port);
+               fdir_match->mask.ipv4.dport =
+                       rte_be_to_cpu_16(mask->hdr.dst_port);
+       }
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_flow_tcp_parse(const struct rte_flow_item *item,
+       struct rte_flow_error *error, bool outer,
+       struct sssnic_ethdev_fdir_flow_match *fdir_match)
+{
+       const struct rte_flow_item_tcp *spec, *mask;
+
+       spec = (const struct rte_flow_item_tcp *)item->spec;
+       mask = (const struct rte_flow_item_tcp *)item->mask;
+
+       if (outer) {
+               if (spec != NULL || mask != NULL) {
+                       rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ITEM, item,
+                               "Both of outer TCP spec and mask must be NULL 
in tunnel flow");
+                       return -rte_errno;
+               }
+
+               return 0;
+       }
+
+       if (fdir_match != NULL) {
+               /* ipv6 match can share ip_proto with ipv4 match */
+               fdir_match->key.ipv4.ip_proto = IPPROTO_TCP;
+               fdir_match->mask.ipv6.ip_proto = 0xff;
+       }
+
+       if (spec == NULL && mask == NULL)
+               return 0;
+
+       if (spec == NULL || mask == NULL) {
+               rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
+                       item, "Invalid TCP spec or mask.");
+               return -rte_errno;
+       }
+
+       if (mask->hdr.sent_seq || mask->hdr.recv_ack || mask->hdr.data_off ||
+               mask->hdr.rx_win || mask->hdr.tcp_flags || mask->hdr.cksum ||
+               mask->hdr.tcp_urp) {
+               rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
+                       item,
+                       "Invalid TCP item, support src_port and dst_port only");
+               return -rte_errno;
+       }
+
+       if (fdir_match != NULL) {
+               /* Other types of fdir match can share sport and dport with 
ipv4 match */
+               fdir_match->key.ipv4.sport =
+                       rte_be_to_cpu_16(spec->hdr.src_port);
+               fdir_match->mask.ipv4.sport =
+                       rte_be_to_cpu_16(mask->hdr.src_port);
+               fdir_match->key.ipv4.dport =
+                       rte_be_to_cpu_16(spec->hdr.dst_port);
+               fdir_match->mask.ipv4.dport =
+                       rte_be_to_cpu_16(mask->hdr.dst_port);
+       }
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_flow_vxlan_parse(const struct rte_flow_item *item,
+       struct rte_flow_error *error,
+       struct sssnic_ethdev_fdir_flow_match *fdir_match)
+{
+       const struct rte_flow_item_vxlan *spec, *mask;
+       uint32_t vni;
+
+       spec = (const struct rte_flow_item_vxlan *)item->spec;
+       mask = (const struct rte_flow_item_vxlan *)item->mask;
+
+       if (spec == NULL && mask == NULL)
+               return 0;
+
+       if (spec == NULL || mask == NULL) {
+               rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
+                       item, "Invalid VXLAN spec or mask");
+               return -rte_errno;
+       }
+
+       /* vxlan-ipv6 match can share vni with vxlan-ipv4 match */
+       if (fdir_match != NULL) {
+               rte_memcpy(((uint8_t *)&vni) + 1, spec->vni, 3);
+               vni = rte_be_to_cpu_32(vni);
+               fdir_match->key.ipv4.vni_w0 = (uint16_t)vni;
+               fdir_match->key.ipv4.vni_w1 = (uint16_t)(vni >> 16);
+               rte_memcpy(((uint8_t *)&vni) + 1, mask->vni, 3);
+               vni = rte_be_to_cpu_32(vni);
+               fdir_match->mask.ipv4.vni_w0 = (uint16_t)vni;
+               fdir_match->mask.ipv4.vni_w1 = (uint16_t)(vni >> 16);
+       }
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_flow_fdir_pattern_parse(const struct rte_flow_item *pattern,
+       struct rte_flow_error *error, bool is_tunnel,
+       struct sssnic_ethdev_fdir_rule *fdir_rule)
+{
+       struct sssnic_ethdev_fdir_flow_match *fdir_match = NULL;
+       const struct rte_flow_item *flow_item;
+       bool outer_ip;
+       int ret = 0;
+
+       fdir_rule->match.type = SSSNIC_ETHDEV_FDIR_MATCH_FLOW;
+       if (fdir_rule != NULL)
+               fdir_match = &fdir_rule->match.flow;
+
+       if (is_tunnel)
+               outer_ip = true;
+       else
+               outer_ip = false;
+
+       flow_item = pattern;
+       while (flow_item->type != RTE_FLOW_ITEM_TYPE_END) {
+               switch (flow_item->type) {
+               case RTE_FLOW_ITEM_TYPE_ETH:
+                       ret = sssnic_ethdev_flow_eth_parse(flow_item, error);
+                       break;
+               case RTE_FLOW_ITEM_TYPE_IPV4:
+                       ret = sssnic_ethdev_flow_ipv4_parse(flow_item, error,
+                               outer_ip, fdir_match);
+                       break;
+               case RTE_FLOW_ITEM_TYPE_IPV6:
+                       ret = sssnic_ethdev_flow_ipv6_parse(flow_item, error,
+                               is_tunnel, fdir_match);
+                       break;
+               case RTE_FLOW_ITEM_TYPE_UDP:
+                       ret = sssnic_ethdev_flow_udp_parse(flow_item, error,
+                               outer_ip, fdir_match);
+                       break;
+               case RTE_FLOW_ITEM_TYPE_TCP:
+                       ret = sssnic_ethdev_flow_tcp_parse(flow_item, error,
+                               outer_ip, fdir_match);
+                       break;
+               case RTE_FLOW_ITEM_TYPE_VXLAN:
+                       ret = sssnic_ethdev_flow_vxlan_parse(flow_item, error,
+                               fdir_match);
+                       outer_ip = false; /* next parsing is inner_ip */
+                       break;
+               default:
+                       break;
+               }
+
+               if (ret != 0)
+                       return ret;
+
+               flow_item++;
+       }
+
+       if (is_tunnel) {
+               if (fdir_match != NULL) {
+                       /* tunnel_type of ipv4 flow_match can share with other 
flow_matches */
+                       fdir_match->key.ipv4.tunnel_type =
+                               SSSNIC_ETHDEV_FDIR_FLOW_TUNNEL_VXLAN;
+                       fdir_match->mask.ipv4.tunnel_type = 0x1;
+               }
+       }
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_flow_attr_parse(const struct rte_flow_attr *attr,
+       struct rte_flow_error *error)
+{
+       if (attr->egress != 0 || attr->priority != 0 || attr->group != 0) {
+               rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ATTR,
+                       attr, "Invalid flow attr, support ingress only");
+               return -rte_errno;
+       }
+
+       if (attr->ingress == 0) {
+               rte_flow_error_set(error, EINVAL,
+                       RTE_FLOW_ERROR_TYPE_ATTR_INGRESS, attr,
+                       "Ingress of flow attr is not set");
+               return -rte_errno;
+       }
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_flow_parse(struct rte_eth_dev *ethdev,
+       const struct rte_flow_attr *attr, const struct rte_flow_item *pattern,
+       const struct rte_flow_action *actions, struct rte_flow_error *error,
+       struct sssnic_ethdev_fdir_rule *fdir_rule)
+{
+       int ret;
+       struct sssnic_ethdev_flow_pattern *flow_pattern;
+
+       flow_pattern = sssnic_ethdev_flow_pattern_lookup(pattern);
+       if (flow_pattern == NULL) {
+               rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
+                       NULL, "Unsupported pattern");
+               return -rte_errno;
+       }
+
+       if (flow_pattern->type == SSSNIC_ETHDEV_FLOW_TYPE_FDIR)
+               ret = sssnic_ethdev_flow_fdir_pattern_parse(pattern, error,
+                       flow_pattern->is_tunnel, fdir_rule);
+       else
+               ret = sssnic_ethdev_flow_ethertype_pattern_parse(pattern, error,
+                       fdir_rule);
+       if (ret != 0)
+               return ret;
+
+       ret = sssnic_ethdev_flow_action_parse(ethdev, actions, error,
+               fdir_rule);
+       if (ret != 0)
+               return ret;
+
+       ret = sssnic_ethdev_flow_attr_parse(attr, error);
+       if (ret != 0)
+               return ret;
+
+       return 0;
+}
+
+static struct rte_flow *
+sssnic_ethdev_flow_create(struct rte_eth_dev *ethdev,
+       const struct rte_flow_attr *attr, const struct rte_flow_item pattern[],
+       const struct rte_flow_action actions[], struct rte_flow_error *error)
+{
+       struct sssnic_ethdev_fdir_rule *rule;
+       int ret;
+
+       rule = sssnic_ethdev_fdir_rule_alloc();
+       if (rule == NULL) {
+               rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+                       NULL, "Failed to allocate fdir rule memory");
+               return NULL;
+       }
+
+       ret = sssnic_ethdev_flow_parse(ethdev, attr, pattern, actions, error,
+               rule);
+       if (ret != 0) {
+               sssnic_ethdev_fdir_rule_free(rule);
+               return NULL;
+       }
+
+       ret = sssnic_ethdev_fdir_rule_add(ethdev, rule);
+       if (ret != 0) {
+               sssnic_ethdev_fdir_rule_free(rule);
+               rte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+                       "Failed to add fdir rule");
+               return NULL;
+       }
+
+       return (struct rte_flow *)rule;
+}
+
+static int
+sssnic_ethdev_flow_destroy(struct rte_eth_dev *ethdev, struct rte_flow *flow,
+       struct rte_flow_error *error)
+{
+       int ret;
+
+       if (flow == NULL) {
+               rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE,
+                       NULL, "Invalid parameter");
+               return -rte_errno;
+       }
+
+       ret = sssnic_ethdev_fdir_rule_del(ethdev,
+               (struct sssnic_ethdev_fdir_rule *)flow);
+
+       if (ret != 0) {
+               rte_flow_error_set(error, EIO, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+                       "Failed to delete fdir rule");
+               return -rte_errno;
+       }
+
+       sssnic_ethdev_fdir_rule_free((struct sssnic_ethdev_fdir_rule *)flow);
+
+       return 0;
+}
+
+static int
+sssnic_ethdev_flow_validate(struct rte_eth_dev *ethdev,
+       const struct rte_flow_attr *attr, const struct rte_flow_item pattern[],
+       const struct rte_flow_action actions[], struct rte_flow_error *error)
+{
+       return sssnic_ethdev_flow_parse(ethdev, attr, pattern, actions, error,
+               NULL);
+}
+
+static int
+sssnic_ethdev_flow_flush(struct rte_eth_dev *ethdev,
+       struct rte_flow_error *error)
+{
+       int ret;
+
+       ret = sssnic_ethdev_fdir_rules_flush(ethdev);
+       if (ret != 0) {
+               rte_flow_error_set(error, EIO, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+                       "Failed to flush fdir rules");
+               return -rte_errno;
+       }
+
+       return 0;
+}
+
+static const struct rte_flow_ops sssnic_ethdev_flow_ops = {
+       .validate = sssnic_ethdev_flow_validate,
+       .create = sssnic_ethdev_flow_create,
+       .destroy = sssnic_ethdev_flow_destroy,
+       .flush = sssnic_ethdev_flow_flush,
+};
+
+int
+sssnic_ethdev_flow_ops_get(struct rte_eth_dev *ethdev,
+       const struct rte_flow_ops **ops)
+{
+       RTE_SET_USED(ethdev);
+
+       *ops = &sssnic_ethdev_flow_ops;
+
+       return 0;
+}
diff --git a/drivers/net/sssnic/sssnic_ethdev_flow.h 
b/drivers/net/sssnic/sssnic_ethdev_flow.h
new file mode 100644
index 0000000000..2812b783e2
--- /dev/null
+++ b/drivers/net/sssnic/sssnic_ethdev_flow.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2022 Shenzhen 3SNIC Information Technology Co., Ltd.
+ */
+
+#ifndef _SSSNIC_ETHDEV_FLOW_H_
+#define _SSSNIC_ETHDEV_FLOW_H_
+
+int sssnic_ethdev_flow_ops_get(struct rte_eth_dev *ethdev,
+       const struct rte_flow_ops **ops);
+
+#endif /* _SSSNIC_ETHDEV_FLOW_H_ */
diff --git a/drivers/net/sssnic/sssnic_ethdev_rx.c 
b/drivers/net/sssnic/sssnic_ethdev_rx.c
index 6c5f209262..46a1d5fd23 100644
--- a/drivers/net/sssnic/sssnic_ethdev_rx.c
+++ b/drivers/net/sssnic/sssnic_ethdev_rx.c
@@ -11,6 +11,7 @@
 #include "sssnic_ethdev.h"
 #include "sssnic_ethdev_rx.h"
 #include "sssnic_ethdev_rss.h"
+#include "sssnic_ethdev_fdir.h"
 #include "base/sssnic_hw.h"
 #include "base/sssnic_workq.h"
 #include "base/sssnic_api.h"
@@ -593,9 +594,18 @@ static int
 sssnic_ethdev_rxq_enable(struct rte_eth_dev *ethdev, uint16_t queue_id)
 {
        struct sssnic_ethdev_rxq *rxq = ethdev->data->rx_queues[queue_id];
+       int ret;
 
        sssnic_ethdev_rxq_pktmbufs_fill(rxq);
 
+       pthread_mutex_lock(&ethdev->data->flow_ops_mutex);
+       ret = sssnic_ethdev_fdir_rules_enable_by_queue(ethdev, queue_id);
+       if (ret)
+               PMD_DRV_LOG(WARNING,
+                       "Failed to enable fdir rules of rxq:%u, port:%u",
+                       queue_id, ethdev->data->port_id);
+       pthread_mutex_unlock(&ethdev->data->flow_ops_mutex);
+
        return 0;
 }
 
@@ -605,6 +615,14 @@ sssnic_ethdev_rxq_disable(struct rte_eth_dev *ethdev, 
uint16_t queue_id)
        struct sssnic_ethdev_rxq *rxq = ethdev->data->rx_queues[queue_id];
        int ret;
 
+       pthread_mutex_lock(&ethdev->data->flow_ops_mutex);
+       ret = sssnic_ethdev_fdir_rules_disable_by_queue(ethdev, queue_id);
+       if (ret != 0)
+               PMD_DRV_LOG(WARNING,
+                       "Failed to disable fdir rules of rxq:%u, port:%u",
+                       queue_id, ethdev->data->port_id);
+       pthread_mutex_unlock(&ethdev->data->flow_ops_mutex);
+
        ret = sssnic_ethdev_rxq_flush(rxq);
        if (ret != 0) {
                PMD_DRV_LOG(ERR, "Failed to flush rxq:%u, port:%u", queue_id,
-- 
2.27.0

Reply via email to