Hi, A general question: does the implementation somehow enforce any specific order of actions? Say, check that ENCAP does not follow DROP? And does it need to enforce, for example, a check that action DECAP is only used when the pattern has matched on the very presence of an encap. header in the packet? (please also see below) On Fri, 8 Aug 2025, Junlong Wang wrote: > From: Bingbin Chen  > > Provide support for ETH, VLAN, IPv4/IPv6, TCP/UDP, VXLAN, > and mask matching, supporting multiple actions > include drop/count/mark/queue/rss,and vxlan decap/encap. > > Signed-off-by: Bingbin Chen  > --- >  doc/guides/nics/features/zxdh.ini  |   16 + >  doc/guides/nics/zxdh.rst           |    1 + >  drivers/net/zxdh/meson.build       |    1 + >  drivers/net/zxdh/zxdh_common.h     |    1 + >  drivers/net/zxdh/zxdh_ethdev.c     |   26 + >  drivers/net/zxdh/zxdh_ethdev.h     |   12 +- >  drivers/net/zxdh/zxdh_ethdev_ops.c |    2 +- >  drivers/net/zxdh/zxdh_ethdev_ops.h |    1 + >  drivers/net/zxdh/zxdh_flow.c       | 2000 ++++++++++++++++++++++++++++ >  drivers/net/zxdh/zxdh_flow.h       |  237 ++++ >  drivers/net/zxdh/zxdh_msg.c        |  264 +++- >  drivers/net/zxdh/zxdh_msg.h        |   31 +- >  drivers/net/zxdh/zxdh_tables.h     |   10 +- >  13 files changed, 2529 insertions(+), 73 deletions(-) >  create mode 100644 drivers/net/zxdh/zxdh_flow.c >  create mode 100644 drivers/net/zxdh/zxdh_flow.h > > diff --git a/doc/guides/nics/features/zxdh.ini b/doc/guides/nics/features/zxdh.ini > index 277e17a584..bd20838676 100644 > --- a/doc/guides/nics/features/zxdh.ini > +++ b/doc/guides/nics/features/zxdh.ini > @@ -34,5 +34,21 @@ Extended stats       = Y >  FW version           = Y >  Module EEPROM dump   = Y >   > +[rte_flow items] > +eth                  = Y > +ipv4                 = Y > +ipv6                 = Y > +sctp                 = Y > +tcp                  = Y > +udp                  = Y > +vlan                 = Y > +vxlan                = Y > + >  [rte_flow actions] >  drop                 = Y > +count                = Y > +mark                 = Y > +queue                = Y > +rss                  = Y > +vxlan_decap          = Y > +vxlan_encap          = Y > diff --git a/doc/guides/nics/zxdh.rst b/doc/guides/nics/zxdh.rst > index 372cb5b44f..47dabde97e 100644 > --- a/doc/guides/nics/zxdh.rst > +++ b/doc/guides/nics/zxdh.rst > @@ -41,6 +41,7 @@ Features of the ZXDH PMD are: >  - Hardware TSO for generic IP or UDP tunnel, including VXLAN >  - Extended statistics query >  - Ingress meter support > +- Flow API >   >   >  Driver compilation and testing > diff --git a/drivers/net/zxdh/meson.build b/drivers/net/zxdh/meson.build > index ec71451a55..7554d4dc60 100644 > --- a/drivers/net/zxdh/meson.build > +++ b/drivers/net/zxdh/meson.build > @@ -24,4 +24,5 @@ sources = files( >          'zxdh_rxtx.c', >          'zxdh_ethdev_ops.c', >          'zxdh_mtr.c', > +        'zxdh_flow.c', >  ) > diff --git a/drivers/net/zxdh/zxdh_common.h b/drivers/net/zxdh/zxdh_common.h > index c151101bbc..6d78ae0273 100644 > --- a/drivers/net/zxdh/zxdh_common.h > +++ b/drivers/net/zxdh/zxdh_common.h > @@ -14,6 +14,7 @@ >  #define ZXDH_VF_LOCK_REG               0x90 >  #define ZXDH_VF_LOCK_ENABLE_MASK       0x1 >  #define ZXDH_ACQUIRE_CHANNEL_NUM_MAX   10 > +#define VF_IDX(pcie_id)     ((pcie_id) & 0xff) >   >  struct zxdh_res_para { >      uint64_t virt_addr; > diff --git a/drivers/net/zxdh/zxdh_ethdev.c b/drivers/net/zxdh/zxdh_ethdev.c > index c7864a8bef..0429bd0333 100644 > --- a/drivers/net/zxdh/zxdh_ethdev.c > +++ b/drivers/net/zxdh/zxdh_ethdev.c > @@ -1272,6 +1272,11 @@ zxdh_dev_close(struct rte_eth_dev *dev) >          return -1; >      } >   > +    if (zxdh_shared_data != NULL) { > +        zxdh_mtr_release(dev); > +        zxdh_flow_release(dev); > +    } > + >      zxdh_intr_release(dev); >      zxdh_np_uninit(dev); >      zxdh_pci_reset(hw); > @@ -1487,6 +1492,7 @@ static const struct eth_dev_ops zxdh_eth_dev_ops = { >      .get_module_eeprom         = zxdh_dev_get_module_eeprom, >      .dev_supported_ptypes_get = zxdh_dev_supported_ptypes_get, >      .mtr_ops_get             = zxdh_meter_ops_get, > +    .flow_ops_get             = zxdh_flow_ops_get, >  }; >   >  static int32_t > @@ -1567,6 +1573,8 @@ zxdh_dtb_dump_res_init(struct zxdh_hw *hw, ZXDH_DEV_INIT_CTRL_T *dpp_ctrl) >          {"sdt_mc_table1",       5 * 1024 * 1024, ZXDH_SDT_MC_TABLE1, NULL}, >          {"sdt_mc_table2",       5 * 1024 * 1024, ZXDH_SDT_MC_TABLE2, NULL}, >          {"sdt_mc_table3",       5 * 1024 * 1024, ZXDH_SDT_MC_TABLE3, NULL}, > +        {"sdt_acl_index_mng",   4 * 1024 * 1024, 30, NULL}, > +        {"sdt_fd_table",        4 * 1024 * 1024, ZXDH_SDT_FD_TABLE, NULL}, >      }; >   >      struct zxdh_dev_shared_data *dev_sd = hw->dev_sd; > @@ -1786,6 +1794,7 @@ zxdh_free_sh_res(void) >          rte_spinlock_lock(&zxdh_shared_data_lock); >          if (zxdh_shared_data != NULL && zxdh_shared_data->init_done && >              (--zxdh_shared_data->dev_refcnt == 0)) { > +            rte_mempool_free(zxdh_shared_data->flow_mp); >              rte_mempool_free(zxdh_shared_data->mtr_mp); >              rte_mempool_free(zxdh_shared_data->mtr_profile_mp); >              rte_mempool_free(zxdh_shared_data->mtr_policy_mp); > @@ -1797,6 +1806,7 @@ zxdh_free_sh_res(void) >  static int >  zxdh_init_sh_res(struct zxdh_shared_data *sd) >  { > +    const char *MZ_ZXDH_FLOW_MP        = "zxdh_flow_mempool"; >      const char *MZ_ZXDH_MTR_MP         = "zxdh_mtr_mempool"; >      const char *MZ_ZXDH_MTR_PROFILE_MP = "zxdh_mtr_profile_mempool"; >      const char *MZ_ZXDH_MTR_POLICY_MP = "zxdh_mtr_policy_mempool"; > @@ -1806,6 +1816,13 @@ zxdh_init_sh_res(struct zxdh_shared_data *sd) >      struct rte_mempool *mtr_policy_mp = NULL; >   >      if (rte_eal_process_type() == RTE_PROC_PRIMARY) { > +        flow_mp = rte_mempool_create(MZ_ZXDH_FLOW_MP, ZXDH_MAX_FLOW_NUM, > +            sizeof(struct zxdh_flow), 64, 0, > +            NULL, NULL, NULL, NULL, SOCKET_ID_ANY, 0); > +        if (flow_mp == NULL) { > +            PMD_DRV_LOG(ERR, "Cannot allocate zxdh flow mempool"); > +            goto error; > +        } >          mtr_mp = rte_mempool_create(MZ_ZXDH_MTR_MP, ZXDH_MAX_MTR_NUM, >              sizeof(struct zxdh_mtr_object), 64, 0, >              NULL, NULL, NULL, NULL, SOCKET_ID_ANY, 0); > @@ -1828,6 +1845,7 @@ zxdh_init_sh_res(struct zxdh_shared_data *sd) >              PMD_DRV_LOG(ERR, "Cannot allocate zxdh mtr profile mempool"); >              goto error; >          } > +        sd->flow_mp = flow_mp; >          sd->mtr_mp = mtr_mp; >          sd->mtr_profile_mp = mtr_profile_mp; >          sd->mtr_policy_mp = mtr_policy_mp; > @@ -1877,6 +1895,7 @@ zxdh_init_once(struct rte_eth_dev *eth_dev) >          ret = zxdh_init_sh_res(sd); >          if (ret != 0) >              goto out; > +        zxdh_flow_global_init(); >          rte_spinlock_init(&g_mtr_res.hw_plcr_res_lock); >          memset(&g_mtr_res, 0, sizeof(g_mtr_res)); >          sd->init_done = true; > @@ -1904,6 +1923,12 @@ zxdh_tbl_entry_offline_destroy(struct zxdh_hw *hw) >          ret = zxdh_np_dtb_hash_offline_delete(hw->dev_id, dtb_data->queueid, sdt_no, 0); >          if (ret) >              PMD_DRV_LOG(ERR, "sdt_no %d delete failed. code:%d ", sdt_no, ret); > + > +        ret = zxdh_np_dtb_acl_offline_delete(hw->dev_id, dtb_data->queueid, > +                    ZXDH_SDT_FD_TABLE, hw->vport.vport, > +                    ZXDH_FLOW_STATS_INGRESS_BASE, 1); > +        if (ret) > +            PMD_DRV_LOG(ERR, "flow offline delete failed. code:%d", ret); >      } >      return ret; >  } > @@ -2153,6 +2178,7 @@ zxdh_eth_dev_init(struct rte_eth_dev *eth_dev) >      if (ret) >          goto err_zxdh_init; >   > +    zxdh_flow_init(eth_dev); >      zxdh_queue_res_get(eth_dev); >      zxdh_msg_cb_reg(hw); >      if (zxdh_priv_res_init(hw) != 0) > diff --git a/drivers/net/zxdh/zxdh_ethdev.h b/drivers/net/zxdh/zxdh_ethdev.h > index f8456f9b22..a2d7b14601 100644 > --- a/drivers/net/zxdh/zxdh_ethdev.h > +++ b/drivers/net/zxdh/zxdh_ethdev.h > @@ -11,6 +11,7 @@ >  #include  >   >  #include "zxdh_mtr.h" > +#include "zxdh_flow.h" >   >  /* ZXDH PCI vendor/device ID. */ >  #define ZXDH_PCI_VENDOR_ID        0x1cf2 > @@ -134,7 +135,10 @@ struct zxdh_hw { >      uint8_t is_pf         : 1, >              switchoffload : 1, >              i_mtr_en      : 1, > -            e_mtr_en      : 1; > +            e_mtr_en      : 1, > +            i_flow_en     : 1, > +            e_flow_en     : 1, > +            vxlan_flow_en : 1; >      uint8_t msg_chan_init; >      uint8_t phyport; >      uint8_t panel_id; > @@ -154,7 +158,10 @@ struct zxdh_hw { >      uint16_t queue_pool_count; >      uint16_t queue_pool_start; >      uint8_t dl_net_hdr_len; > -    uint8_t rsv1[3]; > +    uint16_t vxlan_fd_num; > +    uint8_t rsv1[1]; > + > +    struct dh_flow_list dh_flow_list; >  }; >   >  struct zxdh_dtb_shared_data { > @@ -179,6 +186,7 @@ struct zxdh_shared_data { >      int32_t np_init_done; >      uint32_t dev_refcnt; >      struct zxdh_dtb_shared_data *dtb_data; > +    struct rte_mempool *flow_mp; >      struct rte_mempool *mtr_mp; >      struct rte_mempool *mtr_profile_mp; >      struct rte_mempool *mtr_policy_mp; > diff --git a/drivers/net/zxdh/zxdh_ethdev_ops.c b/drivers/net/zxdh/zxdh_ethdev_ops.c > index 39078b99d2..da32512b03 100644 > --- a/drivers/net/zxdh/zxdh_ethdev_ops.c > +++ b/drivers/net/zxdh/zxdh_ethdev_ops.c > @@ -1120,7 +1120,7 @@ zxdh_dev_rss_reta_update(struct rte_eth_dev *dev, >      return ret; >  } >   > -static uint16_t > +uint16_t >  zxdh_hw_qid_to_logic_qid(struct rte_eth_dev *dev, uint16_t qid) >  { >      struct zxdh_hw *priv = (struct zxdh_hw *)dev->data->dev_private; > diff --git a/drivers/net/zxdh/zxdh_ethdev_ops.h b/drivers/net/zxdh/zxdh_ethdev_ops.h > index 6015b3de59..86db6efe40 100644 > --- a/drivers/net/zxdh/zxdh_ethdev_ops.h > +++ b/drivers/net/zxdh/zxdh_ethdev_ops.h > @@ -142,5 +142,6 @@ int zxdh_dev_fw_version_get(struct rte_eth_dev *dev, char *fw_version, size_t fw >  int zxdh_dev_get_module_info(struct rte_eth_dev *dev, struct rte_eth_dev_module_info *modinfo); >  int zxdh_dev_get_module_eeprom(struct rte_eth_dev *dev, struct rte_dev_eeprom_info *info); >  int zxdh_meter_ops_get(struct rte_eth_dev *dev, void *arg); > +uint16_t zxdh_hw_qid_to_logic_qid(struct rte_eth_dev *dev, uint16_t qid); >   >  #endif /* ZXDH_ETHDEV_OPS_H */ > diff --git a/drivers/net/zxdh/zxdh_flow.c b/drivers/net/zxdh/zxdh_flow.c > new file mode 100644 > index 0000000000..04c616c1e2 > --- /dev/null > +++ b/drivers/net/zxdh/zxdh_flow.c > @@ -0,0 +1,2000 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright(c) 2024 ZTE Corporation Why 2024? > + */ > + > +#include  > +#include  > +#include  > +#include  > +#include  > +#include  > +#include  > + > +#include  > +#include  > +#include  > +#include  > +#include  > +#include  > +#include  > +#include  > + > +#include "zxdh_ethdev.h" > +#include "zxdh_logs.h" > +#include "zxdh_flow.h" > +#include "zxdh_tables.h" > +#include "zxdh_ethdev_ops.h" > +#include "zxdh_np.h" > +#include "zxdh_msg.h" > + > +#define ZXDH_IPV6_FRAG_HEADER     44 > +#define ZXDH_TENANT_ARRAY_NUM     3 > +#define ZXDH_VLAN_TCI_MASK       0xFFFF > +#define ZXDH_VLAN_PRI_MASK       0xE000 > +#define ZXDH_VLAN_CFI_MASK       0x1000 > +#define ZXDH_VLAN_VID_MASK       0x0FFF > +#define MAX_STRING_LEN           8192 > +#define FLOW_INGRESS             0 > +#define FLOW_EGRESS              1 > +#define MAX_ENCAP1_NUM           (256) > +#define INVALID_HANDLEIDX        0xffff > +#define ACTION_VXLAN_ENCAP_ITEMS_NUM (6) > +static struct dh_engine_list flow_engine_list = TAILQ_HEAD_INITIALIZER(flow_engine_list); > +static struct count_res flow_count_ref[MAX_FLOW_COUNT_NUM]; > +static rte_spinlock_t fd_hw_res_lock = RTE_SPINLOCK_INITIALIZER; > +static uint8_t fd_hwres_bitmap[ZXDH_MAX_FLOW_NUM] = {0}; > + > +#define MKDUMPSTR(buf, buf_size, cur_len, ...) \ > +do { \ > +    if ((cur_len) >= (buf_size)) \ > +        break; \ > +    (cur_len) += snprintf((buf) + (cur_len), (buf_size) - (cur_len), __VA_ARGS__); \ > +} while (0) > + > +static inline void > +print_ether_addr(const char *what, const struct rte_ether_addr *eth_addr, > +         char print_buf[], uint32_t buf_size, uint32_t *cur_len) > +{ > +    char buf[RTE_ETHER_ADDR_FMT_SIZE]; > + > +    rte_ether_format_addr(buf, RTE_ETHER_ADDR_FMT_SIZE, eth_addr); > +    MKDUMPSTR(print_buf, buf_size, *cur_len, "%s%s", what, buf); > +} > + > +static inline void > +zxdh_fd_flow_free_dtbentry(ZXDH_DTB_USER_ENTRY_T *dtb_entry) > +{ > +    rte_free(dtb_entry->p_entry_data); > +    dtb_entry->p_entry_data = NULL; > +    dtb_entry->sdt_no = 0; > +} > + > +static void > +data_bitwise(void *data, int bytecnt) > +{ > +    int i; > +    uint32_t *temp = (uint32_t *)data; > +    int remain = bytecnt % 4; > +    for (i = 0; i < (bytecnt >> 2); i++)    { > +        *(temp) = ~*(temp); > +        temp++; > +    } > + > +    if (remain) { > +        for (i = 0; i < remain; i++) { > +            uint8_t *tmp = (uint8_t *)temp; > +            *(uint8_t *)tmp = ~*(uint8_t *)tmp; > +            tmp++; > +        } > +    } > +} > + > +static void > +zxdh_adjust_flow_op_rsp_memory_layout(void *old_data, > +        size_t old_size, void *new_data) > +{ > +    rte_memcpy(new_data, old_data, sizeof(struct zxdh_flow)); > +    memset((char *)new_data + sizeof(struct zxdh_flow), 0, 4); > +    rte_memcpy((char *)new_data + sizeof(struct zxdh_flow) + 4, > +        (char *)old_data + sizeof(struct zxdh_flow), > +        old_size - sizeof(struct zxdh_flow)); > +} > + > +void zxdh_flow_global_init(void) > +{ > +    int i; > +    for (i = 0; i < MAX_FLOW_COUNT_NUM; i++) { > +        rte_spinlock_init(&flow_count_ref[i].count_lock); > +        flow_count_ref[i].count_ref = 0; Just a question: does this absolutely need to be PMD-global and not per-device? > +    } > +} > + > +static void > +__entry_dump(char *print_buf, uint32_t buf_size, > +        uint32_t *cur_len, struct fd_flow_key *key) > +{ > +    print_ether_addr("\nL2\t  dst=", &key->mac_dst, print_buf, buf_size, cur_len); > +    print_ether_addr(" - src=", &key->mac_src, print_buf, buf_size, cur_len); > +    MKDUMPSTR(print_buf, buf_size, *cur_len, " -eth type=0x%04x", key->ether_type); Just to make sure: is this in big-endian or little-endian? The original value of EtherType supposedly comes from RTE flow item spec as a big-endian value. > +    MKDUMPSTR(print_buf, buf_size, *cur_len, > +        " -vlan_pri=0x%02x -vlan_vlanid=0x%04x  -vlan_tci=0x%04x ", > +        key->cvlan_pri, key->cvlan_vlanid, key->vlan_tci); > +    MKDUMPSTR(print_buf, buf_size, *cur_len, > +        " -vni=0x%02x 0x%02x 0x%02x\n", key->vni[0], key->vni[1], key->vni[2]); > +    MKDUMPSTR(print_buf, buf_size, *cur_len, > +        "L3\t  dstip=0x%08x 0x%08x 0x%08x 0x%08x("IPv6_BYTES_FMT")\n", > +        *(uint32_t *)key->dst_ip, *((uint32_t *)key->dst_ip + 1), > +        *((uint32_t *)key->dst_ip + 2), > +        *((uint32_t *)key->dst_ip + 3), > +        IPv6_BYTES(key->dst_ip)); > +    MKDUMPSTR(print_buf, buf_size, *cur_len, > +        "\t  srcip=0x%08x 0x%08x 0x%08x 0x%08x("IPv6_BYTES_FMT")\n", > +        *((uint32_t *)key->src_ip), *((uint32_t *)key->src_ip + 1), > +        *((uint32_t *)key->src_ip + 2), > +        *((uint32_t *)key->src_ip + 3), > +        IPv6_BYTES(key->src_ip)); > +    MKDUMPSTR(print_buf, buf_size, *cur_len, > +        "  \t  tos=0x%02x -nw-proto=0x%02x -frag-flag %u\n", > +        key->tos, key->nw_proto, key->frag_flag); > +    MKDUMPSTR(print_buf, buf_size, *cur_len, > +        "L4\t  dstport=0x%04x -srcport=0x%04x", key->tp_dst, key->tp_src); Same question here. > +} > + > +static void > +__result_dump(char *print_buf, uint32_t buf_size, > +        uint32_t *cur_len, struct fd_flow_result *res) > +{ > +    MKDUMPSTR(print_buf, buf_size, *cur_len, " -hit_flag = 0x%04x", res->hit_flag); > +    MKDUMPSTR(print_buf, buf_size, *cur_len, " -action_idx = 0x%02x", res->action_idx); > +    MKDUMPSTR(print_buf, buf_size, *cur_len, " -qid = 0x%04x", res->qid); > +    MKDUMPSTR(print_buf, buf_size, *cur_len, " -mark_id = 0x%08x", res->mark_fd_id); > +    MKDUMPSTR(print_buf, buf_size, *cur_len, " -count_id = 0x%02x", res->countid); > +} > + > +static void offlow_key_dump(struct fd_flow_key *key, struct fd_flow_key *key_mask, FILE *file) > +{ > +    char print_buf[MAX_STRING_LEN]; > +    uint32_t buf_size = MAX_STRING_LEN; > +    uint32_t cur_len = 0; > + > +    MKDUMPSTR(print_buf, buf_size, cur_len, "offload key:\n\t"); > +    __entry_dump(print_buf, buf_size, &cur_len, key); > + > +    MKDUMPSTR(print_buf, buf_size, cur_len, "\noffload key_mask:\n\t"); > +    __entry_dump(print_buf, buf_size, &cur_len, key_mask); > + > +    PMD_DRV_LOG(INFO, "%s", print_buf); > +    MKDUMPSTR(print_buf, buf_size, cur_len, "\n"); > +    if (file) > +        fputs(print_buf, file); > +} > + > +static void offlow_result_dump(struct fd_flow_result *res, FILE *file) > +{ > +    char print_buf[MAX_STRING_LEN]; > +    uint32_t buf_size = MAX_STRING_LEN; > +    uint32_t cur_len = 0; > + > +    MKDUMPSTR(print_buf, buf_size, cur_len, "offload result:\n"); > +    __result_dump(print_buf, buf_size, &cur_len, res); > +    PMD_DRV_LOG(INFO, "%s", print_buf); > +    PMD_DRV_LOG(INFO, "memdump : ===result ==="); > +    MKDUMPSTR(print_buf, buf_size, cur_len, "\n"); > +    if (file) > +        fputs(print_buf, file); > +} > + > +static int > +set_flow_enable(struct rte_eth_dev *dev, uint8_t dir, > +        bool enable, struct rte_flow_error *error) > +{ > +    struct zxdh_hw *priv = dev->data->dev_private; > +    struct zxdh_port_attr_table port_attr = {0}; > +    int ret = 0; > + > +    if (priv->is_pf) { > +        ret = zxdh_get_port_attr(priv, priv->vport.vport, &port_attr); > +        if (ret) { > +            PMD_DRV_LOG(ERR, "get port_attr failed"); > +            return -1; No need to set 'rte_flow_error' here? Just asking. > +        } > +        port_attr.fd_enable = enable; > + > +        ret = zxdh_set_port_attr(priv, priv->vport.vport, &port_attr); > +        if (ret) { > +            PMD_DRV_LOG(ERR, "write port_attr failed"); > +            return -1; Same question here. May be the author intended to use below 'if (ret)' block. > +        } > +    } else { > +        struct zxdh_msg_info msg_info = {0}; > +        struct zxdh_port_attr_set_msg *attr_msg = &msg_info.data.port_attr_msg; > + > +        attr_msg->mode  = ZXDH_PORT_FD_EN_OFF_FLAG; > +        attr_msg->value = enable; > +        zxdh_msg_head_build(priv, ZXDH_PORT_ATTRS_SET, &msg_info); > +        ret = zxdh_vf_send_msg_to_pf(dev, &msg_info, sizeof(msg_info), NULL, 0); > +    } > +    if (ret) { > +        PMD_DRV_LOG(ERR, "port %d flow enable failed", priv->port_id); > +        return -rte_flow_error_set(error, EEXIST, > +                    RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, > +                    "Meter  enable failed."); > +    } > +    if (dir == FLOW_INGRESS) > +        priv->i_flow_en = !!enable; > +    else > +        priv->e_flow_en = !!enable; > + > +    return ret; > +} > + > +static int > +set_vxlan_enable(struct rte_eth_dev *dev, bool enable, struct rte_flow_error *error) > +{ > +    struct zxdh_hw *priv = dev->data->dev_private; > +    struct zxdh_port_attr_table port_attr = {0}; > +    int ret = 0; > + > +    if (priv->vxlan_flow_en == !!enable) > +        return 0; > +    if (priv->is_pf) { > +        ret = zxdh_get_port_attr(priv, priv->vport.vport, &port_attr); > +        if (ret) { > +            PMD_DRV_LOG(ERR, "get port_attr failed"); > +            return -1; > +        } > +        port_attr.fd_enable = enable; > + > +        ret = zxdh_set_port_attr(priv, priv->vport.vport, &port_attr); > +        if (ret) { > +            PMD_DRV_LOG(ERR, "write port_attr failed"); > +            return -1; > +        } > +    } else { > +        struct zxdh_msg_info msg_info = {0}; > +        struct zxdh_port_attr_set_msg *attr_msg = &msg_info.data.port_attr_msg; > + > +        attr_msg->mode  = ZXDH_PORT_VXLAN_OFFLOAD_EN_OFF; > +        attr_msg->value = enable; > + > +        zxdh_msg_head_build(priv, ZXDH_PORT_ATTRS_SET, &msg_info); > +        ret = zxdh_vf_send_msg_to_pf(dev, &msg_info, sizeof(msg_info), NULL, 0); > +    } > +    if (ret) { > +        PMD_DRV_LOG(ERR, "port %d vxlan flow enable failed", priv->port_id); > +        return -rte_flow_error_set(error, EEXIST, > +                    RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, > +                    "vxlan offload enable failed."); > +    } > +    priv->vxlan_flow_en = !!enable; > +    return ret; > +} > + > +void zxdh_register_flow_engine(struct dh_flow_engine *engine) > +{ > +    TAILQ_INSERT_TAIL(&flow_engine_list, engine, node); > +} > + > +static void zxdh_flow_free(struct zxdh_flow *dh_flow) > +{ > +    if (dh_flow) > +        rte_mempool_put(zxdh_shared_data->flow_mp, dh_flow); > +} > + > +static struct dh_flow_engine *zxdh_get_flow_engine(struct rte_eth_dev *dev __rte_unused) > +{ > +    struct dh_flow_engine *engine = NULL; > +    void *temp; > + > +    RTE_TAILQ_FOREACH_SAFE(engine, &flow_engine_list, node, temp) { > +        if (engine->type  == FLOW_TYPE_FD_TCAM) > +            break; > +    } > +    return engine; > +} > + > +static int > +zxdh_flow_validate(struct rte_eth_dev *dev, > +             const struct rte_flow_attr *attr, > +             const struct rte_flow_item  *pattern, > +             const struct rte_flow_action *actions, > +             struct rte_flow_error *error) > +{ > +    struct dh_flow_engine *flow_engine = NULL; > + > +    if (!pattern) { > +        rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_NUM, > +                   NULL, "NULL pattern."); > +        return -rte_errno; > +    } > + > +    if (!actions) { > +        rte_flow_error_set(error, EINVAL, > +                   RTE_FLOW_ERROR_TYPE_ACTION_NUM, > +                   NULL, "NULL action."); > +        return -rte_errno; > +    } > + > +    if (!attr) { > +        rte_flow_error_set(error, EINVAL, > +                   RTE_FLOW_ERROR_TYPE_ATTR, > +                   NULL, "NULL attribute."); > +        return -rte_errno; > +    } > +    flow_engine = zxdh_get_flow_engine(dev); > +    if (flow_engine == NULL || flow_engine->parse_pattern_action == NULL) { > +        rte_flow_error_set(error, EINVAL, > +                   RTE_FLOW_ERROR_TYPE_UNSPECIFIED, > +                   NULL, "cannot find valid flow engine."); > +        return -rte_errno; > +    } > +    if (flow_engine->parse_pattern_action(dev, attr, pattern, actions, error, NULL) != 0) > +        return -rte_errno; > +    return 0; > +} > + > +static struct zxdh_flow *flow_exist_check(struct rte_eth_dev *dev, struct zxdh_flow *dh_flow) > +{ > +    struct zxdh_hw *hw = dev->data->dev_private; > +    struct rte_flow *entry; > +    struct zxdh_flow *entry_flow; > + > +    TAILQ_FOREACH(entry, &hw->dh_flow_list, next) { > +        entry_flow = (struct zxdh_flow *)entry->driver_flow; > +        if ((memcmp(&entry_flow->flowentry.fd_flow.key, &dh_flow->flowentry.fd_flow.key, > +                 sizeof(struct fd_flow_key)) == 0)  && > +            (memcmp(&entry_flow->flowentry.fd_flow.key_mask, > +                &dh_flow->flowentry.fd_flow.key_mask, > +                 sizeof(struct fd_flow_key)) == 0)) { > +            return entry_flow; > +        } > +    } > +    return NULL; > +} > + > +static struct rte_flow * > +zxdh_flow_create(struct rte_eth_dev *dev, > +         const struct rte_flow_attr *attr, > +         const struct rte_flow_item pattern[], > +         const struct rte_flow_action actions[], > +         struct rte_flow_error *error) > +{ > +    struct zxdh_hw *hw = dev->data->dev_private; > +    struct rte_flow *flow = NULL; > +    struct zxdh_flow *dh_flow = NULL; > +    int ret = 0; > +    struct dh_flow_engine *flow_engine = NULL; > + > +    flow_engine = zxdh_get_flow_engine(dev); > + > +    if (flow_engine == NULL || > +        flow_engine->parse_pattern_action == NULL || > +        flow_engine->apply == NULL) { > +        rte_flow_error_set(error, EINVAL, > +                   RTE_FLOW_ERROR_TYPE_UNSPECIFIED, > +                   NULL, "cannot find valid flow engine."); > +        return NULL; > +    } > + > +    flow = rte_zmalloc("rte_flow", sizeof(struct rte_flow), 0); > +    if (!flow) { > +        rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, "flow malloc failed"); > +        return NULL; > +    } > +    ret = rte_mempool_get(zxdh_shared_data->flow_mp, (void **)&dh_flow); > +    if (ret) { > +        rte_flow_error_set(error, ENOMEM, > +                   RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                    "Failed to allocate memory from flowmp"); > +        goto free_flow; > +    } > +    memset(dh_flow, 0, sizeof(struct zxdh_flow)); > +    if (flow_engine->parse_pattern_action(dev, attr, pattern, actions, error, dh_flow) != 0) { > +        PMD_DRV_LOG(ERR, "parse_pattern_action failed zxdh_created failed"); > +        goto free_flow; > +    } > + > +    if (flow_exist_check(dev, dh_flow) != NULL) { > +        rte_flow_error_set(error, EINVAL, > +                   RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                    "flow repeat .no add again"); Consider: "duplicate entry; will not add". > +        goto free_flow; > +    } > + > +    ret = flow_engine->apply(dev, dh_flow, error, hw->vport.vport, hw->pcie_id); > +    if (ret) { > +        PMD_DRV_LOG(ERR, "apply failed flow created failed"); Consider may be: "flow creation failed: failed to apply". > +        goto free_flow; > +    } > +    flow->driver_flow = dh_flow; > +    flow->port_id = dev->data->port_id; > +    flow->type = ZXDH_FLOW_GROUP_TCAM; > +    TAILQ_INSERT_TAIL(&hw->dh_flow_list, flow, next); > + > +    if (hw->i_flow_en == 0) { > +        ret = set_flow_enable(dev, FLOW_INGRESS, 1, error); > +        if (ret < 0) { May be invoke 'flow_engine->destroy'? I may be wrong, -- just asking. And may be need to remove 'flow' from 'dh_flow_list' here? > +            PMD_DRV_LOG(ERR, "set flow enable failed"); > +            goto free_flow; > +        } > +    } > +    return flow; > +free_flow: > +    zxdh_flow_free(dh_flow); > +    rte_free(flow); > +    return NULL; > +} > + > +static int > +zxdh_flow_destroy(struct rte_eth_dev *dev, > +          struct rte_flow *flow, > +          struct rte_flow_error *error) > +{ > +    struct zxdh_hw *priv = dev->data->dev_private; > +    struct zxdh_flow *dh_flow = NULL; > +    int ret = 0; > +    struct dh_flow_engine *flow_engine = NULL; > + > +    flow_engine = zxdh_get_flow_engine(dev); > +    if (flow_engine == NULL || > +        flow_engine->destroy == NULL) { > +        rte_flow_error_set(error, EINVAL, > +                 RTE_FLOW_ERROR_TYPE_UNSPECIFIED, > +                 NULL, "cannot find valid flow engine."); > +        return -rte_errno; > +    } > +    if (flow->driver_flow) > +        dh_flow = (struct zxdh_flow *)flow->driver_flow; > + > +    if (dh_flow == NULL) { > +        PMD_DRV_LOG(ERR, "invalid flow"); > +        rte_flow_error_set(error, EINVAL, > +                 RTE_FLOW_ERROR_TYPE_UNSPECIFIED, > +                 NULL, "invalid flow"); > +        return -1; > +    } > +    ret = flow_engine->destroy(dev, dh_flow, error, priv->vport.vport, priv->pcie_id); > +    if (ret) { > +        rte_flow_error_set(error, -ret, > +                   RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                   "Failed to destroy flow."); > +        return -rte_errno; > +    } > +    TAILQ_REMOVE(&priv->dh_flow_list, flow, next); > +    zxdh_flow_free(dh_flow); > +    rte_free(flow); > + > +    if (TAILQ_EMPTY(&priv->dh_flow_list)) { > +        ret = set_flow_enable(dev, FLOW_INGRESS, 0, error); > +        if (ret) { > +            PMD_DRV_LOG(ERR, "clear flow enable failed"); > +            return -rte_errno; > +        } > +    } > +    return ret; > +} > + > + > +static int > +zxdh_flow_query(struct rte_eth_dev *dev, > +        struct rte_flow *flow, > +        const struct rte_flow_action *actions, > +        void *data, struct rte_flow_error *error) > +{ > +    struct zxdh_flow *dh_flow; > +    int ret = 0; > +    struct dh_flow_engine *flow_engine = NULL; > + > +    flow_engine = zxdh_get_flow_engine(dev); > + > +    if (flow_engine == NULL || > +        flow_engine->query_count == NULL) { > +        rte_flow_error_set(error, EINVAL, > +                   RTE_FLOW_ERROR_TYPE_UNSPECIFIED, > +                   NULL, "cannot find valid flow engine."); > +        return -rte_errno; > +    } > + > +    if (flow->driver_flow) > +        dh_flow = (struct zxdh_flow *)flow->driver_flow; > +    dh_flow = (struct zxdh_flow *)flow->driver_flow; Confused. So does the code need this 'if' check or can it go without it? > +    if (dh_flow == NULL) { > +        PMD_DRV_LOG(ERR, "flow is not exist"); Consider: "flow does not exist". > +        return -1; > +    } > + > +    for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) { > +        switch (actions->type) { > +        case RTE_FLOW_ACTION_TYPE_VOID: > +            break; > +        case RTE_FLOW_ACTION_TYPE_COUNT: > +            ret = flow_engine->query_count(dev, dh_flow, > +                         (struct rte_flow_query_count *)data, error); > +            break; > +        default: > +            ret = rte_flow_error_set(error, ENOTSUP, > +                    RTE_FLOW_ERROR_TYPE_ACTION, > +                    actions, > +                    "action not supported"); Perhaps: "action does not support QUERY". > +            goto out; > +        } > +    } > +out: > +    if (ret) > +        PMD_DRV_LOG(ERR, "flow query failed"); > +    return ret; > +} > + > +static int zxdh_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error) > +{ > +    struct rte_flow *flow; > +    struct zxdh_flow *dh_flow = NULL; > +    struct zxdh_hw *hw = dev->data->dev_private; > +    struct zxdh_dtb_shared_data *dtb_data = &hw->dev_sd->dtb_sd; > +    struct dh_flow_engine *flow_engine = NULL; > +    struct zxdh_msg_info msg_info = {0}; > +    uint8_t zxdh_msg_reply_info[ZXDH_ST_SZ_BYTES(msg_reply_info)] = {0}; > +    int ret = 0; > + > +    flow_engine = zxdh_get_flow_engine(dev); > +    if (flow_engine == NULL) { > +        PMD_DRV_LOG(ERR, "get flow engine failed"); > +        return -1; > +    } > +    ret = set_flow_enable(dev, FLOW_INGRESS, 0, error); > +    if (ret) { > +        PMD_DRV_LOG(ERR, "clear flow enable failed"); > +        return ret; > +    } > + > +    ret = set_vxlan_enable(dev, 0, error); > +    if (ret) > +        PMD_DRV_LOG(ERR, "clear vxlan enable failed"); > +    hw->vxlan_fd_num = 0; > + > +    if (hw->is_pf) { > +        ret = zxdh_np_dtb_acl_offline_delete(hw->dev_id, dtb_data->queueid, > +                    ZXDH_SDT_FD_TABLE, hw->vport.vport, > +                    ZXDH_FLOW_STATS_INGRESS_BASE, 1); > +        if (ret) > +            PMD_DRV_LOG(ERR, "%s flush failed. code:%d", dev->data->name, ret); > +    } else { > +        zxdh_msg_head_build(hw, ZXDH_FLOW_HW_FLUSH, &msg_info); > +        ret = zxdh_vf_send_msg_to_pf(dev, &msg_info, sizeof(struct zxdh_msg_info), > +            (void *)zxdh_msg_reply_info, ZXDH_ST_SZ_BYTES(msg_reply_info)); > +        if (ret) { > +            PMD_DRV_LOG(ERR, "port %d flow op %d flush failed ret %d", > +                hw->port_id, ZXDH_FLOW_HW_FLUSH, ret); > +            return -1; > +        } > +    } > + > +    /* Remove all flows */ > +    while ((flow = TAILQ_FIRST(&hw->dh_flow_list))) { > +        TAILQ_REMOVE(&hw->dh_flow_list, flow, next); > +        if (flow->driver_flow) > +            dh_flow = (struct zxdh_flow *)flow->driver_flow; > +        if (dh_flow == NULL) { > +            PMD_DRV_LOG(ERR, "Invalid flow Failed to destroy flow."); > +            ret = rte_flow_error_set(error, ENOTSUP, > +                    RTE_FLOW_ERROR_TYPE_HANDLE, > +                    NULL, > +                    "Invalid flow ,flush failed"); > +            return ret; > +        } > + > +        zxdh_flow_free(dh_flow); > +        rte_free(flow); > +    } > +    return ret; > +} > + > +static void > +handle_res_dump(struct rte_eth_dev *dev) > +{ > +    struct zxdh_hw *priv =  dev->data->dev_private; > +    uint16_t hwres_base = priv->vport.pfid << 10; > +    uint16_t hwres_cnt = ZXDH_MAX_FLOW_NUM >> 1; > +    uint16_t i; > + > +    PMD_DRV_LOG(DEBUG, "hwres_base %d", hwres_base); > +    rte_spinlock_lock(&fd_hw_res_lock); > +    for (i = 0; i < hwres_cnt; i++) { > +        if (fd_hwres_bitmap[hwres_base + i] == 1) > +            PMD_DRV_LOG(DEBUG, "used idx %d", i + hwres_base); > +    } > +    rte_spinlock_unlock(&fd_hw_res_lock); > +} > + > +static int > +zxdh_flow_dev_dump(struct rte_eth_dev *dev, > +            struct rte_flow *flow, > +            FILE *file, > +            struct rte_flow_error *error __rte_unused) > +{ > +    struct zxdh_hw *hw =  dev->data->dev_private; > +    struct rte_flow *entry; > +    struct zxdh_flow *entry_flow; > +    uint32_t dtb_qid = 0; > +    uint32_t entry_num = 0; > +    uint16_t ret = 0; > +    ZXDH_DTB_ACL_ENTRY_INFO_T *fd_entry = NULL; > +    uint8_t *key = NULL; > +    uint8_t *key_mask = NULL; > +    uint8_t *result = NULL; > + > +    if (flow) { > +        entry_flow = flow_exist_check(dev, (struct zxdh_flow *)flow->driver_flow); > +        if (entry_flow) { > +            PMD_DRV_LOG(DEBUG, "handle idx %d:", entry_flow->flowentry.hw_idx); > +            offlow_key_dump(&entry_flow->flowentry.fd_flow.key, > +                &entry_flow->flowentry.fd_flow.key_mask, file); > +            offlow_result_dump(&entry_flow->flowentry.fd_flow.result, file); > +        } > +    } else { > +        if (hw->is_pf) { > +            dtb_qid = hw->dev_sd->dtb_sd.queueid; > +            fd_entry = calloc(1, sizeof(ZXDH_DTB_ACL_ENTRY_INFO_T) * ZXDH_MAX_FLOW_NUM); > +            key = calloc(1, sizeof(struct fd_flow_key) * ZXDH_MAX_FLOW_NUM); > +            key_mask = calloc(1, sizeof(struct fd_flow_key) * ZXDH_MAX_FLOW_NUM); > +            result = calloc(1, sizeof(struct fd_flow_result) * ZXDH_MAX_FLOW_NUM); > +            if (!fd_entry || !key || !key_mask || !result) { > +                PMD_DRV_LOG(ERR, "fd_entry malloc failed!"); > +                goto end; > +            } > + > +            for (int i = 0; i < ZXDH_MAX_FLOW_NUM; i++) { > +                fd_entry[i].key_data = key + i * sizeof(struct fd_flow_key); > +                fd_entry[i].key_mask = key_mask + i * sizeof(struct fd_flow_key); > +                fd_entry[i].p_as_rslt = result + i * sizeof(struct fd_flow_result); > +            } > +            ret = zxdh_np_dtb_acl_table_dump_by_vport(hw->dev_id, dtb_qid, > +                        ZXDH_SDT_FD_TABLE, hw->vport.vport, &entry_num, > +                        (uint8_t *)fd_entry); > +            if (ret) { > +                PMD_DRV_LOG(ERR, "dpp_dtb_acl_table_dump_by_vport failed!"); > +                goto end; > +            } > +            for (uint32_t i = 0; i < entry_num; i++) { > +                offlow_key_dump((struct fd_flow_key *)fd_entry[i].key_data, > +                    (struct fd_flow_key *)fd_entry[i].key_mask, file); > +                offlow_result_dump((struct fd_flow_result *)fd_entry[i].p_as_rslt, > +                        file); > +            } > +            free(result); > +            free(key_mask); > +            free(key); > +            free(fd_entry); > +        } else { > +            entry = calloc(1, sizeof(struct rte_flow)); > +            entry_flow = calloc(1, sizeof(struct zxdh_flow)); > +            TAILQ_FOREACH(entry, &hw->dh_flow_list, next) { > +                entry_flow = (struct zxdh_flow *)entry->driver_flow; > +                offlow_key_dump(&entry_flow->flowentry.fd_flow.key, > +                        &entry_flow->flowentry.fd_flow.key_mask, file); > +                offlow_result_dump(&entry_flow->flowentry.fd_flow.result, file); > +            } > +            free(entry_flow); > +            free(entry); > +        } > +    } > +    handle_res_dump(dev); > + > +    return 0; > +end: > +    free(result); > +    free(key_mask); > +    free(key); > +    free(fd_entry); > +    return -1; > +} > + > +static int32_t > +get_available_handle(struct zxdh_hw *hw, uint16_t vport) > +{ > +    int ret = 0; > +    uint32_t handle_idx = 0; > + > +    ret = zxdh_np_dtb_acl_index_request(hw->dev_id, ZXDH_SDT_FD_TABLE, vport, &handle_idx); > +    if (ret) { > +        PMD_DRV_LOG(ERR, "Failed to allocate memory for hw!"); > +        return INVALID_HANDLEIDX; > +    } > +    return handle_idx; > +} > + > +static int free_handle(struct zxdh_hw *hw, uint16_t handle_idx, uint16_t vport) > +{ > +    int ret = zxdh_np_dtb_acl_index_release(hw->dev_id, ZXDH_SDT_FD_TABLE, vport, handle_idx); > + > +    if (ret) { > +        PMD_DRV_LOG(ERR, "Failed to free handle_idx %d for hw!", handle_idx); > +        return -1; > +    } > +    return 0; > +} > + > +static uint16_t > +zxdh_encap0_to_dtbentry(struct zxdh_hw *hw __rte_unused, > +            struct zxdh_flow *dh_flow, > +            ZXDH_DTB_USER_ENTRY_T *dtb_entry) > +{ > +    ZXDH_DTB_ERAM_ENTRY_INFO_T *dtb_eram_entry; > +    dtb_eram_entry = rte_zmalloc(NULL, sizeof(ZXDH_DTB_ERAM_ENTRY_INFO_T), 0); > + > +    if (dtb_eram_entry == NULL) > +        return INVALID_HANDLEIDX; > + > +    dtb_eram_entry->index = dh_flow->flowentry.fd_flow.result.encap0_index * 2; > +    dtb_eram_entry->p_data = (uint32_t *)&dh_flow->encap0; > + > +    dtb_entry->sdt_no = ZXDH_SDT_TUNNEL_ENCAP0_TABLE; > +    dtb_entry->p_entry_data = dtb_eram_entry; > +    return 0; > +} > + > +static uint16_t > +zxdh_encap0_ip_to_dtbentry(struct zxdh_hw *hw __rte_unused, > +            struct zxdh_flow *dh_flow, > +            ZXDH_DTB_USER_ENTRY_T *dtb_entry) > +{ > +    ZXDH_DTB_ERAM_ENTRY_INFO_T *dtb_eram_entry; > +    dtb_eram_entry = rte_zmalloc(NULL, sizeof(ZXDH_DTB_ERAM_ENTRY_INFO_T), 0); > + > +    if (dtb_eram_entry == NULL) > +        return INVALID_HANDLEIDX; > + > +    dtb_eram_entry->index = dh_flow->flowentry.fd_flow.result.encap0_index * 2 + 1; > +    dtb_eram_entry->p_data = (uint32_t *)&dh_flow->encap0.dip; > +    dtb_entry->sdt_no = ZXDH_SDT_TUNNEL_ENCAP0_TABLE; > +    dtb_entry->p_entry_data = dtb_eram_entry; > +    return 0; > +} > + > +static uint16_t zxdh_encap1_to_dtbentry(struct zxdh_hw *hw __rte_unused, > +                             struct zxdh_flow *dh_flow, > +                             ZXDH_DTB_USER_ENTRY_T *dtb_entry) > +{ > +    ZXDH_DTB_ERAM_ENTRY_INFO_T *dtb_eram_entry; > +    dtb_eram_entry = rte_zmalloc(NULL, sizeof(ZXDH_DTB_ERAM_ENTRY_INFO_T), 0); > + > +    if (dtb_eram_entry == NULL) > +        return INVALID_HANDLEIDX; > + > +    if (dh_flow->encap0.ethtype == 0) > +        dtb_eram_entry->index = dh_flow->flowentry.fd_flow.result.encap1_index * 4; > +    else > +        dtb_eram_entry->index = dh_flow->flowentry.fd_flow.result.encap1_index * 4 + 1; > + > +    dtb_eram_entry->p_data = (uint32_t *)&dh_flow->encap1; > + > +    dtb_entry->sdt_no = ZXDH_SDT_TUNNEL_ENCAP1_TABLE; > +    dtb_entry->p_entry_data = dtb_eram_entry; > +    return 0; > +} > + > +static uint16_t > +zxdh_encap1_ip_to_dtbentry(struct zxdh_hw *hw __rte_unused, > +            struct zxdh_flow *dh_flow, > +            ZXDH_DTB_USER_ENTRY_T *dtb_entry) > +{ > +    ZXDH_DTB_ERAM_ENTRY_INFO_T *dtb_eram_entry; > +    dtb_eram_entry = rte_zmalloc(NULL, sizeof(ZXDH_DTB_ERAM_ENTRY_INFO_T), 0); > + > +    if (dtb_eram_entry == NULL) > +        return INVALID_HANDLEIDX; > +    if (dh_flow->encap0.ethtype == 0) > +        dtb_eram_entry->index = dh_flow->flowentry.fd_flow.result.encap1_index * 4 + 2; > +    else > +        dtb_eram_entry->index = dh_flow->flowentry.fd_flow.result.encap1_index * 4 + 3; > +    dtb_eram_entry->p_data = (uint32_t *)&dh_flow->encap1.sip; > +    dtb_entry->sdt_no = ZXDH_SDT_TUNNEL_ENCAP1_TABLE; > +    dtb_entry->p_entry_data = dtb_eram_entry; > +    return 0; > +} > + > +static int zxdh_hw_encap_insert(struct rte_eth_dev *dev, > +                    struct zxdh_flow *dh_flow, > +                    struct rte_flow_error *error) > +{ > +    uint32_t ret; > +    struct zxdh_hw *hw = dev->data->dev_private; > +    uint32_t dtb_qid = hw->dev_sd->dtb_sd.queueid; > +    ZXDH_DTB_USER_ENTRY_T dtb_entry = {0}; > + > +    zxdh_encap0_to_dtbentry(hw, dh_flow, &dtb_entry); > +    ret = zxdh_np_dtb_table_entry_write(hw->dev_id, dtb_qid, 1, &dtb_entry); > +    zxdh_fd_flow_free_dtbentry(&dtb_entry); > +    if (ret) { > +        rte_flow_error_set(error, EINVAL, > +                            RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                            "write to hw failed"); > +        return -1; > +    } > + > +    zxdh_encap0_ip_to_dtbentry(hw, dh_flow, &dtb_entry); > +    ret = zxdh_np_dtb_table_entry_write(hw->dev_id, dtb_qid, 1, &dtb_entry); > +    zxdh_fd_flow_free_dtbentry(&dtb_entry); > +    if (ret) { > +        rte_flow_error_set(error, EINVAL, > +                RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                "write to hw failed"); > +        return -1; > +    } > + > +    zxdh_encap1_to_dtbentry(hw, dh_flow, &dtb_entry); > +    ret = zxdh_np_dtb_table_entry_write(hw->dev_id, dtb_qid, 1, &dtb_entry); > +    zxdh_fd_flow_free_dtbentry(&dtb_entry); > +    if (ret) { > +        rte_flow_error_set(error, EINVAL, > +                    RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                    "write to hw failed"); > +        return -1; > +    } > + > +    zxdh_encap1_ip_to_dtbentry(hw, dh_flow, &dtb_entry); > +    ret = zxdh_np_dtb_table_entry_write(hw->dev_id, dtb_qid, 1, &dtb_entry); > +    zxdh_fd_flow_free_dtbentry(&dtb_entry); > +    if (ret) { > +        rte_flow_error_set(error, EINVAL, > +                    RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                    "write to hw failed"); > +        return -1; > +    } > +    return 0; > +} > + > +static uint16_t > +zxdh_fd_flow_to_dtbentry(struct zxdh_hw *hw __rte_unused, > +        struct zxdh_flow_info *fdflow, > +        ZXDH_DTB_USER_ENTRY_T *dtb_entry) > +{ > +    ZXDH_DTB_ACL_ENTRY_INFO_T *dtb_acl_entry; > +    uint16_t handle_idx = 0; > +    dtb_acl_entry = rte_zmalloc("fdflow_dtbentry", sizeof(ZXDH_DTB_ACL_ENTRY_INFO_T), 0); > + > +    if (dtb_acl_entry == NULL) > +        return INVALID_HANDLEIDX; > + > +    dtb_acl_entry->key_data = (uint8_t *)&fdflow->fd_flow.key; > +    dtb_acl_entry->key_mask = (uint8_t *)&fdflow->fd_flow.key_mask; > +    dtb_acl_entry->p_as_rslt = (uint8_t *)&fdflow->fd_flow.result; > + > +    handle_idx = fdflow->hw_idx; > + > +    if (handle_idx >= ZXDH_MAX_FLOW_NUM) { > +        rte_free(dtb_acl_entry); > +        return INVALID_HANDLEIDX; > +    } > +    dtb_acl_entry->handle = handle_idx; > +    dtb_entry->sdt_no = ZXDH_SDT_FD_TABLE; > +    dtb_entry->p_entry_data = dtb_acl_entry; > +    return handle_idx; > +} > + > +static int zxdh_hw_flow_insert(struct rte_eth_dev *dev, > +                                struct zxdh_flow *dh_flow, > +                                struct rte_flow_error *error, > +                                uint16_t vport) > +{ > +    struct zxdh_hw *hw = dev->data->dev_private; > +    uint32_t dtb_qid = hw->dev_sd->dtb_sd.queueid; > +    ZXDH_DTB_USER_ENTRY_T dtb_entry = {0}; > +    uint32_t ret; > +    uint16_t handle_idx; > + > +    struct zxdh_flow_info *flow = &dh_flow->flowentry; > +    handle_idx = zxdh_fd_flow_to_dtbentry(hw, flow, &dtb_entry); > +    if (handle_idx == INVALID_HANDLEIDX) { > +        rte_flow_error_set(error, EINVAL, > +                         RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                         "Failed to allocate memory for hw"); > +        return -1; > +    } > +    ret = zxdh_np_dtb_table_entry_write(hw->dev_id, dtb_qid, 1, &dtb_entry); > +    zxdh_fd_flow_free_dtbentry(&dtb_entry); > +    if (ret) { > +        ret = free_handle(hw, handle_idx, vport); > +        if (ret) { > +            rte_flow_error_set(error, EINVAL, > +                         RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                         "release handle_idx to hw failed"); > +            return -1; > +        } > +        rte_flow_error_set(error, EINVAL, > +                         RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                         "write to hw failed"); > +        return -1; > +    } > +    dh_flow->flowentry.hw_idx = handle_idx; > +    return 0; > +} > + > +static int > +hw_count_query(struct zxdh_hw *hw, uint32_t countid, bool clear, > +        struct flow_stats *fstats, struct rte_flow_error *error) > +{ > +    uint32_t stats_id = 0; > +    int ret = 0; > +    stats_id = countid; > +    if (stats_id >= ZXDH_MAX_FLOW_NUM) { > +        PMD_DRV_LOG(DEBUG, "query count id %d invalid", stats_id); > +        ret = rte_flow_error_set(error, ENODEV, > +                 RTE_FLOW_ERROR_TYPE_HANDLE, > +                 NULL, > +                 "query count id invalid"); > +        return -rte_errno; > +    } > +    PMD_DRV_LOG(DEBUG, "query count id %d,clear %d ", stats_id, clear); > +    if (!clear) > +        ret = zxdh_np_dtb_stats_get(hw->dev_id, hw->dev_sd->dtb_sd.queueid, 1, > +                    stats_id + ZXDH_FLOW_STATS_INGRESS_BASE, > +                    (uint32_t *)fstats); > +    else > +        ret = zxdh_np_stat_ppu_cnt_get_ex(hw->dev_id, 1, > +                    stats_id + ZXDH_FLOW_STATS_INGRESS_BASE, > +                    1, (uint32_t *)fstats); > +    if (ret) > +        rte_flow_error_set(error, EINVAL, > +                 RTE_FLOW_ERROR_TYPE_ACTION, NULL, > +                     "fail to get flow stats"); > +    return ret; > +} > + > +static int > +count_deref(struct zxdh_hw *hw, uint32_t countid, > +        struct rte_flow_error *error) > +{ > +    int ret = 0; > +    struct count_res *count_res = &flow_count_ref[countid]; > +    struct flow_stats fstats = {0}; > + > +    rte_spinlock_lock(&count_res->count_lock); > + > +    if (count_res->count_ref >= 1) { > +        count_res->count_ref--; > +    } else { > +        rte_spinlock_unlock(&count_res->count_lock); > +        return rte_flow_error_set(error, ENOTSUP, > +                     RTE_FLOW_ERROR_TYPE_ACTION_CONF, > +                     NULL, > +                     "count deref underflow"); > +    } > +    if (count_res->count_ref == 0) > +        ret = hw_count_query(hw, countid, 1, &fstats, error); > + > +    rte_spinlock_unlock(&count_res->count_lock); > +    return ret; > +} > + > +static int > +count_ref(struct zxdh_hw *hw, uint32_t countid, struct rte_flow_error *error) > +{ > +    int ret = 0; > +    struct count_res *count_res = &flow_count_ref[countid]; > +    struct flow_stats fstats = {0}; > + > +    rte_spinlock_lock(&count_res->count_lock); > +    if (count_res->count_ref < 255) { > +        count_res->count_ref++; > +    } else { > +        rte_spinlock_unlock(&count_res->count_lock); > +        return rte_flow_error_set(error, ENOTSUP, > +                     RTE_FLOW_ERROR_TYPE_ACTION_CONF, > +                     NULL, > +                     "count ref overflow"); > +    } > + > +    if (count_res->count_ref == 1) > +        ret = hw_count_query(hw, countid, 1, &fstats, error); > + > +    rte_spinlock_unlock(&count_res->count_lock); > +    return ret; > +} > + > +int > +pf_fd_hw_apply(struct rte_eth_dev *dev, struct zxdh_flow *dh_flow, > +        struct rte_flow_error *error, uint16_t vport, uint16_t pcieid) > +{ > +    int ret = 0; > +    struct zxdh_hw *hw = dev->data->dev_private; > +    uint8_t vf_index = 0; > +    uint8_t action_bits = dh_flow->flowentry.fd_flow.result.action_idx; > +    uint32_t countid  = MAX_FLOW_COUNT_NUM; > +    uint32_t handle_idx = 0; > +    union zxdh_virport_num port = {0}; > + > +    port.vport = vport; > +    handle_idx = get_available_handle(hw, vport); > +    if (handle_idx >= ZXDH_MAX_FLOW_NUM) { > +        rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE, NULL, "Failed to allocate memory for hw"); > +        return -1; > +    } > +    dh_flow->flowentry.hw_idx = handle_idx; > +    if ((action_bits & (1 << FD_ACTION_COUNT_BIT)) != 0) { > +        countid = handle_idx; > +        dh_flow->flowentry.fd_flow.result.countid = countid; > +    } > + > +    if ((action_bits & (1 << FD_ACTION_VXLAN_ENCAP)) != 0) { > +        dh_flow->flowentry.fd_flow.result.encap0_index = handle_idx; > +        if (!port.vf_flag) { > +            dh_flow->flowentry.fd_flow.result.encap1_index = > +                hw->hash_search_index * MAX_ENCAP1_NUM; > +        } else { > +            vf_index = VF_IDX(pcieid); > +            if (vf_index < (ZXDH_MAX_VF - 1)) { > +                dh_flow->flowentry.fd_flow.result.encap1_index = > +                    hw->hash_search_index * MAX_ENCAP1_NUM + vf_index + 1; > +            } else { > +                rte_flow_error_set(error, EINVAL, > +                        RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                        "encap1 vf_index is too big"); > +                return -1; > +            } > +        } > +        PMD_DRV_LOG(DEBUG, "encap_index (%d)(%d)", > +                dh_flow->flowentry.fd_flow.result.encap0_index, > +                dh_flow->flowentry.fd_flow.result.encap1_index); > +        if (zxdh_hw_encap_insert(dev, dh_flow, error) != 0) > +            return -1; > +    } > +    ret = zxdh_hw_flow_insert(dev, dh_flow, error, vport); > +    if (!ret && countid < MAX_FLOW_COUNT_NUM) > +        ret = count_ref(hw, countid, error); > + > +    if (!ret) { > +        if (!port.vf_flag) { > +            if (((action_bits & (1 << FD_ACTION_VXLAN_ENCAP)) != 0) || > +                ((action_bits & (1 << FD_ACTION_VXLAN_DECAP)) != 0)) { > +                hw->vxlan_fd_num++; > +                if (hw->vxlan_fd_num == 1) > +                    set_vxlan_enable(dev, 1, error); > +            } > +        } > +    } > + > +    return ret; > +} > + > +static int > +zxdh_hw_flow_del(struct rte_eth_dev *dev, > +                            struct zxdh_flow *dh_flow, > +                            struct rte_flow_error *error, > +                            uint16_t vport) > +{ > +    struct zxdh_flow_info *flow = &dh_flow->flowentry; > +    ZXDH_DTB_USER_ENTRY_T dtb_entry = {0}; > +    struct zxdh_hw *hw = dev->data->dev_private; > +    uint32_t dtb_qid = hw->dev_sd->dtb_sd.queueid; > +    uint32_t ret; > +    uint16_t handle_idx; > + > +    handle_idx = zxdh_fd_flow_to_dtbentry(hw, flow, &dtb_entry); > +    if (handle_idx >= ZXDH_MAX_FLOW_NUM) { > +        rte_flow_error_set(error, EINVAL, > +                         RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                         "Failed to allocate memory for hw"); > +        return -1; > +    } > +    ret = zxdh_np_dtb_table_entry_delete(hw->dev_id, dtb_qid, 1, &dtb_entry); > +    zxdh_fd_flow_free_dtbentry(&dtb_entry); > +    if (ret) { > +        rte_flow_error_set(error, EINVAL, > +                         RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                         "delete to hw failed"); > +        return -1; > +    } > +    ret = free_handle(hw, handle_idx, vport); > +    if (ret) { > +        rte_flow_error_set(error, EINVAL, > +                        RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                        "release handle_idx to hw failed"); > +        return -1; > +    } > +    PMD_DRV_LOG(DEBUG, "release handle_idx to hw succ! %d", handle_idx); What does 'succ' stand for? Succinct? Succumb? Success? > +    return ret; > +} > + > +int > +pf_fd_hw_destroy(struct rte_eth_dev *dev, struct zxdh_flow *dh_flow, > +        struct rte_flow_error *error, uint16_t vport, > +        uint16_t pcieid __rte_unused) > +{ > +    struct zxdh_hw *hw = dev->data->dev_private; > +    union zxdh_virport_num port = {0}; > +    int ret = 0; > + > +    port.vport = vport; > +    ret = zxdh_hw_flow_del(dev, dh_flow, error, vport); > +    PMD_DRV_LOG(DEBUG, "destroy handle id %d", dh_flow->flowentry.hw_idx); > +    if (!ret) { > +        uint8_t action_bits = dh_flow->flowentry.fd_flow.result.action_idx; > +        uint32_t countid; > +        countid = dh_flow->flowentry.hw_idx; > +        if ((action_bits & (1 << FD_ACTION_COUNT_BIT)) != 0) > +            ret = count_deref(hw, countid, error); > +        if (!port.vf_flag) { > +            if (((action_bits & (1 << FD_ACTION_VXLAN_ENCAP)) != 0) || > +                ((action_bits & (1 << FD_ACTION_VXLAN_DECAP)) != 0)) { > +                hw->vxlan_fd_num--; > +                if (hw->vxlan_fd_num == 0) > +                    set_vxlan_enable(dev, 0, error); > +            } > +        } > +    } > +    return ret; > +} > + > +static int > +zxdh_hw_flow_query(struct rte_eth_dev *dev, struct zxdh_flow *dh_flow, > +        struct rte_flow_error *error) > +{ > +    struct zxdh_hw *hw = dev->data->dev_private; > +    int ret = 0; > +    struct zxdh_flow_info *flow = &dh_flow->flowentry; > +    ZXDH_DTB_USER_ENTRY_T dtb_entry; > +    uint16_t handle_idx; > + > +    handle_idx = zxdh_fd_flow_to_dtbentry(hw, flow, &dtb_entry); > +    if (handle_idx >= ZXDH_MAX_FLOW_NUM) { > +        rte_flow_error_set(error, EINVAL, > +                         RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                         "Failed to build hw entry for query"); > +        ret = -1; > +        goto free_res; > +    } > +    ret = zxdh_np_dtb_table_entry_get(hw->dev_id, hw->dev_sd->dtb_sd.queueid, &dtb_entry, 0); > +    if (ret != 0) { > +        rte_flow_error_set(error, EINVAL, > +                         RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                         "Failed query  entry from hw "); Double-space in the error message. > +        goto free_res; > +    } > + > +free_res: > +    zxdh_fd_flow_free_dtbentry(&dtb_entry); > + > +    return ret; > +} > + > +int > +pf_fd_hw_query_count(struct rte_eth_dev *dev, > +            struct zxdh_flow *flow, > +            struct rte_flow_query_count *count, > +            struct rte_flow_error *error) > +{ > +    struct zxdh_hw *hw =  dev->data->dev_private; > +    struct flow_stats  fstats = {0}; > +    int ret = 0; > +    uint32_t countid; > + > +    memset(&flow->flowentry.fd_flow.result, 0, sizeof(struct fd_flow_result)); > +    ret = zxdh_hw_flow_query(dev, flow, error); > +    if (ret) { > +        ret = rte_flow_error_set(error, ENODEV, > +                 RTE_FLOW_ERROR_TYPE_HANDLE, > +                 NULL, > +                 "query failed"); > +        return -rte_errno; > +    } > +    countid = flow->flowentry.hw_idx; > +    if (countid >= ZXDH_MAX_FLOW_NUM) { > +        ret = rte_flow_error_set(error, ENODEV, > +                 RTE_FLOW_ERROR_TYPE_HANDLE, > +                 NULL, > +                 "query count id invalid"); > +        return -rte_errno; > +    } > +    ret = hw_count_query(hw, countid, 0, &fstats, error); > +    if (ret) { > +        rte_flow_error_set(error, EINVAL, > +                 RTE_FLOW_ERROR_TYPE_ACTION, NULL, > +                     "fail to get flow stats"); > +            return ret; > +    } > +    count->bytes = (uint64_t)(rte_le_to_cpu_32(fstats.hit_bytes_hi)) << 32 | > +                    rte_le_to_cpu_32(fstats.hit_bytes_lo); > +    count->hits = (uint64_t)(rte_le_to_cpu_32(fstats.hit_pkts_hi)) << 32 | > +                    rte_le_to_cpu_32(fstats.hit_pkts_lo); Shouldn't this also check for 'count->reset' (input field)? If the HW does not support resetting the counter upon query, perhaps reject the query then? Also, this sets 'bytes' and 'hits', but not indicate the values are valid by setting 'bytes_set' and 'hits_set'. Why? Please take a look at other PMDs. > +    return ret; > +} > + > +static int > +fd_flow_parse_attr(struct rte_eth_dev *dev __rte_unused, > +        const struct rte_flow_attr *attr, > +        struct rte_flow_error *error, > +        struct zxdh_flow *dh_flow) > +{ > +    /* Not supported */ > +    if (attr->priority) { > +        rte_flow_error_set(error, EINVAL, > +                   RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY, > +                   attr, "Not support priority."); > +        return -rte_errno; > +    } > + > +    /* Not supported */ > +    if (attr->group >= MAX_GROUP) { > +        rte_flow_error_set(error, EINVAL, > +                   RTE_FLOW_ERROR_TYPE_ATTR_GROUP, > +                   attr, "Not support group."); > +        return -rte_errno; > +    } > + > +    if (dh_flow) { > +        dh_flow->group = attr->group; > +        dh_flow->direct = (attr->ingress == 1) ? 0 : 1; > +        dh_flow->pri = attr->priority; > +    } > + > +    return 0; > +} > + > +static int fd_flow_parse_pattern(struct rte_eth_dev *dev, const struct rte_flow_item *items, > +             struct rte_flow_error *error, struct zxdh_flow *dh_flow) > +{ > +    struct zxdh_hw *priv = dev->data->dev_private; > +    struct zxdh_flow_info *flow = NULL; > +    const struct rte_flow_item *item; > +    const struct rte_flow_item_eth *eth_spec, *eth_mask; > +    const struct rte_flow_item_vlan *vlan_spec, *vlan_mask; > +    const struct rte_flow_item_ipv4 *ipv4_spec, *ipv4_mask; > +    const struct rte_flow_item_ipv6 *ipv6_spec = NULL, *ipv6_mask = NULL; > +    const struct rte_flow_item_tcp *tcp_spec, *tcp_mask; > +    const struct rte_flow_item_udp *udp_spec, *udp_mask; > +    const struct rte_flow_item_sctp *sctp_spec, *sctp_mask; > +    const struct rte_flow_item_vxlan *vxlan_spec, *vxlan_mask; > +    struct fd_flow_key *key, *key_mask; > + > +    if (dh_flow) { > +        flow = &dh_flow->flowentry; > +    } else { > +        flow = rte_zmalloc("dh_flow", sizeof(*flow), 0); > +        if (flow == NULL) { > +            rte_flow_error_set(error, EINVAL, > +                         RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                         "Failed to allocate memory "); > +            return -rte_errno; > +        } > +    } > + > +    key = &flow->fd_flow.key; > +    key_mask = &flow->fd_flow.key_mask; > +    key->vfid = rte_cpu_to_be_16(priv->vfid); > +    key_mask->vfid  = 0xffff; > +    for (; items->type != RTE_FLOW_ITEM_TYPE_END; items++) { > +        item = items; > +        if (items->last) { > +            rte_flow_error_set(error, EINVAL, > +                     RTE_FLOW_ERROR_TYPE_ITEM, > +                     items, > +                     "Not support range"); > +            return -rte_errno; > +        } > + > +        switch (item->type) { > +        case RTE_FLOW_ITEM_TYPE_ETH: > +            eth_spec = item->spec; > +            eth_mask = item->mask; > +            if (eth_spec && eth_mask) { > +                key->mac_dst = eth_spec->dst; > +                key->mac_src  = eth_spec->src; > +                key_mask->mac_dst  = eth_mask->dst; > +                key_mask->mac_src  = eth_mask->src; > + > +                if (eth_mask->type == 0xffff) { > +                    key->ether_type = eth_spec->type; > +                    key_mask->ether_type = eth_mask->type; > +                } > +            } 1) If both 'spec' and 'mask' are 'NULL', shouldn't the code set some broader match on the sole presence of a Ethernet header? 2) If 'mask' is 'NULL' and 'spec' is not (or vice versa), isn't this an error? > +            break; > +        case RTE_FLOW_ITEM_TYPE_VLAN: > +            vlan_spec = item->spec; > +            vlan_mask = item->mask; > +            if (vlan_spec && vlan_mask) { > +                key->vlan_tci  = vlan_spec->tci; > +                key_mask->vlan_tci = vlan_mask->tci; > +            } Similar questions here. For example, the user having provided the item VLAN but having omitted both 'spec' and 'mask' can be translated into setting the VLAN EtherType in 'key->ether_type', with an appropriate check of whether this key has already been set to an overlapping value by the previous (ETH) item. > +            break; > +        case RTE_FLOW_ITEM_TYPE_IPV4: > +            ipv4_spec = item->spec; > +            ipv4_mask = item->mask; > + > +            if (ipv4_spec && ipv4_mask) { > +                /* Check IPv4 mask and update input set */ > +                if (ipv4_mask->hdr.version_ihl || > +                    ipv4_mask->hdr.total_length || > +                    ipv4_mask->hdr.packet_id || > +                    ipv4_mask->hdr.hdr_checksum || > +                    ipv4_mask->hdr.time_to_live) { > +                    rte_flow_error_set(error, EINVAL, > +                             RTE_FLOW_ERROR_TYPE_ITEM, > +                             item, > +                             "Invalid IPv4 mask."); > +                    return -rte_errno; > +                } > +                    /* Get the filter info */ > +                key->nw_proto = > +                        ipv4_spec->hdr.next_proto_id; > +                key->tos = > +                        ipv4_spec->hdr.type_of_service; > +                key_mask->nw_proto = > +                        ipv4_mask->hdr.next_proto_id; > +                key_mask->tos = > +                        ipv4_mask->hdr.type_of_service; > +                key->frag_flag = (ipv4_spec->hdr.fragment_offset != 0) ? 1 : 0; > +                key_mask->frag_flag = (ipv4_mask->hdr.fragment_offset != 0) ? 1 : 0; > +                rte_memcpy((uint32_t *)key->src_ip + 3, > +                             &ipv4_spec->hdr.src_addr, 4); > +                rte_memcpy((uint32_t *)key->dst_ip + 3, > +                             &ipv4_spec->hdr.dst_addr, 4); > +                rte_memcpy((uint32_t *)key_mask->src_ip + 3, > +                             &ipv4_mask->hdr.src_addr, 4); > +                rte_memcpy((uint32_t *)key_mask->dst_ip + 3, > +                             &ipv4_mask->hdr.dst_addr, 4); > +            } > +            break; > +        case RTE_FLOW_ITEM_TYPE_IPV6: > +            ipv6_spec = item->spec; > +            ipv6_mask = item->mask; > + > +            if (ipv6_spec && ipv6_mask) { > +                /* Check IPv6 mask and update input set */ > +                if (ipv6_mask->hdr.payload_len || > +                     ipv6_mask->hdr.hop_limits == UINT8_MAX) { > +                    rte_flow_error_set(error, EINVAL, > +                        RTE_FLOW_ERROR_TYPE_ITEM, > +                        item, > +                        "Invalid IPv6 mask"); > +                    return -rte_errno; > +                } > +                key->tc = > +                    (uint8_t)((ipv6_spec->hdr.vtc_flow & > +                                RTE_IPV6_HDR_TC_MASK) >> > +                                RTE_IPV6_HDR_TC_SHIFT); > +                key_mask->tc = > +                    (uint8_t)((ipv6_mask->hdr.vtc_flow & > +                                RTE_IPV6_HDR_TC_MASK) >> > +                                RTE_IPV6_HDR_TC_SHIFT); > + > +                key->nw_proto = ipv6_spec->hdr.proto; > +                key_mask->nw_proto = ipv6_mask->hdr.proto; > + > +                rte_memcpy(key->src_ip, > +                             &ipv6_spec->hdr.src_addr, 16); > +                rte_memcpy(key->dst_ip, > +                             &ipv6_spec->hdr.dst_addr, 16); > +                rte_memcpy(key_mask->src_ip, > +                             &ipv6_mask->hdr.src_addr, 16); > +                rte_memcpy(key_mask->dst_ip, > +                             &ipv6_mask->hdr.dst_addr, 16); > +            } > +            break; > +        case RTE_FLOW_ITEM_TYPE_TCP: > +            tcp_spec = item->spec; > +            tcp_mask = item->mask; > + > +            if (tcp_spec && tcp_mask) { > +                /* Check TCP mask and update input set */ > +                if (tcp_mask->hdr.sent_seq || > +                    tcp_mask->hdr.recv_ack || > +                    tcp_mask->hdr.data_off || > +                    tcp_mask->hdr.tcp_flags || > +                    tcp_mask->hdr.rx_win || > +                    tcp_mask->hdr.cksum || > +                    tcp_mask->hdr.tcp_urp || > +                    (tcp_mask->hdr.src_port && > +                    tcp_mask->hdr.src_port != UINT16_MAX) || > +                    (tcp_mask->hdr.dst_port && > +                    tcp_mask->hdr.dst_port != UINT16_MAX)) { > +                    rte_flow_error_set(error, EINVAL, > +                                 RTE_FLOW_ERROR_TYPE_ITEM, > +                                 item, > +                                 "Invalid TCP mask"); > +                    return -rte_errno; > +                } > + > +                key->tp_src = tcp_spec->hdr.src_port; > +                key_mask->tp_src = tcp_mask->hdr.src_port; > + > +                key->tp_dst = tcp_spec->hdr.dst_port; > +                key_mask->tp_dst = tcp_mask->hdr.dst_port; > +            } > +            break; > +        case RTE_FLOW_ITEM_TYPE_UDP: > +            udp_spec = item->spec; > +            udp_mask = item->mask; > + > +            if (udp_spec && udp_mask) { > +                /* Check UDP mask and update input set*/ > +                if (udp_mask->hdr.dgram_len || > +                    udp_mask->hdr.dgram_cksum || > +                    (udp_mask->hdr.src_port && > +                    udp_mask->hdr.src_port != UINT16_MAX) || > +                    (udp_mask->hdr.dst_port && > +                    udp_mask->hdr.dst_port != UINT16_MAX)) { > +                    rte_flow_error_set(error, EINVAL, > +                                     RTE_FLOW_ERROR_TYPE_ITEM, > +                                     item, > +                                     "Invalid UDP mask"); > +                    return -rte_errno; > +                } > + > +                key->tp_src = udp_spec->hdr.src_port; > +                key_mask->tp_src = udp_mask->hdr.src_port; > + > +                key->tp_dst = udp_spec->hdr.dst_port; > +                key_mask->tp_dst = udp_mask->hdr.dst_port; > +            } > +            break; > +        case RTE_FLOW_ITEM_TYPE_SCTP: > +            sctp_spec = item->spec; > +            sctp_mask = item->mask; > + > +            if (!(sctp_spec && sctp_mask)) > +                break; > + > +            /* Check SCTP mask and update input set */ > +            if (sctp_mask->hdr.cksum) { > +                rte_flow_error_set(error, EINVAL, > +                           RTE_FLOW_ERROR_TYPE_ITEM, > +                           item, > +                           "Invalid sctp mask"); > +                return -rte_errno; > +            } > + > +            /* Mask for SCTP src/dst ports not supported */ > +            if (sctp_mask->hdr.src_port && > +                sctp_mask->hdr.src_port != UINT16_MAX) > +                return -rte_errno; > +            if (sctp_mask->hdr.dst_port && > +                sctp_mask->hdr.dst_port != UINT16_MAX) > +                return -rte_errno; > + > +            key->tp_src = sctp_spec->hdr.src_port; > +            key_mask->tp_src = sctp_mask->hdr.src_port; > +            key->tp_dst = sctp_spec->hdr.dst_port; > +            key_mask->tp_dst = sctp_mask->hdr.dst_port; > +            break; > +        case RTE_FLOW_ITEM_TYPE_VXLAN: > +        { > +            vxlan_spec = item->spec; > +            vxlan_mask = item->mask; > +            static const struct rte_flow_item_vxlan flow_item_vxlan_mask = { > +                .vni = {0xff, 0xff, 0xff}, > +            }; > +            if (!(vxlan_spec && vxlan_mask)) > +                break; > +            if (memcmp(vxlan_mask, &flow_item_vxlan_mask, > +                sizeof(struct rte_flow_item_vxlan))) { > +                rte_flow_error_set(error, EINVAL, > +                                 RTE_FLOW_ERROR_TYPE_ITEM, > +                                 item, > +                                 "Invalid vxlan mask"); > +                    return -rte_errno; > +            } > +            rte_memcpy(key->vni, vxlan_spec->vni, 3); > +            rte_memcpy(key_mask->vni, vxlan_mask->vni, 3); > +            break; > +        } > +        default: > +                return rte_flow_error_set(error, ENOTSUP, > +                                     RTE_FLOW_ERROR_TYPE_ITEM, > +                                     NULL, "item not supported"); How about item of type VOID? > +        } > +    } > + > +    data_bitwise(key_mask, sizeof(*key_mask)); > +    return 0; > +} > + > +static inline int > +validate_action_rss(struct rte_eth_dev *dev, > +             const struct rte_flow_action *action, > +             struct rte_flow_error *error) > +{ > +    const struct rte_flow_action_rss *rss = action->conf; > + > +    if (rss->func != RTE_ETH_HASH_FUNCTION_DEFAULT && > +        rss->func != RTE_ETH_HASH_FUNCTION_TOEPLITZ) > +        return rte_flow_error_set(error, ENOTSUP, > +                      RTE_FLOW_ERROR_TYPE_ACTION_CONF, > +                      &rss->func, > +                    "RSS hash function not supported"); > +    if (rss->level > 1) > +        return rte_flow_error_set(error, ENOTSUP, > +                      RTE_FLOW_ERROR_TYPE_ACTION_CONF, > +                      &rss->level, > +                      "tunnel RSS is not supported"); > +    /* allow RSS key_len 0 in case of NULL (default) RSS key. */ > +    if (rss->key_len == 0 && rss->key != NULL) > +        return rte_flow_error_set(error, ENOTSUP, > +                      RTE_FLOW_ERROR_TYPE_ACTION_CONF, > +                      &rss->key_len, > +                      "RSS hash key length 0"); > +    if (rss->key_len > 0 && rss->key_len < ZXDH_RSS_HASH_KEY_LEN) > +        return rte_flow_error_set(error, ENOTSUP, > +                      RTE_FLOW_ERROR_TYPE_ACTION_CONF, > +                      &rss->key_len, > +                      "RSS hash key too small"); May be useful to indicate which size it should be to succeed. > +    if (rss->key_len > ZXDH_RSS_HASH_KEY_LEN) > +        return rte_flow_error_set(error, ENOTSUP, > +                      RTE_FLOW_ERROR_TYPE_ACTION_CONF, > +                      &rss->key_len, > +                      "RSS hash key too large"); Same here. > +    if (rss->queue_num > dev->data->nb_rx_queues) > +        return rte_flow_error_set(error, ENOTSUP, > +                      RTE_FLOW_ERROR_TYPE_ACTION_CONF, > +                      &rss->queue_num, > +                      "number of queues too large"); If 'nb_rx_queues' is, for example, 3 and if the user passes 'rss->queue' with values [0, 1, 0, 1], this will fail, however, the indices are valid. Strange. > +    if (!rss->queue_num) > +        return rte_flow_error_set(error, EINVAL, > +                      RTE_FLOW_ERROR_TYPE_ACTION_CONF, > +                      NULL, "No queues configured"); > +    return 0; > +} > + > +static int > +fd_flow_parse_vxlan_encap(struct rte_eth_dev *dev __rte_unused, > +        const struct rte_flow_item *item, > +        struct zxdh_flow *dh_flow) > +{ > +    const struct rte_flow_item *items; > +    const struct rte_flow_item_eth *item_eth; > +    const struct rte_flow_item_vlan *item_vlan; > +    const struct rte_flow_item_ipv4 *item_ipv4; > +    const struct rte_flow_item_ipv6 *item_ipv6; > +    const struct rte_flow_item_udp *item_udp; > +    const struct rte_flow_item_vxlan *item_vxlan; > +    uint32_t i = 0; > +    rte_be32_t addr; > + > +    for (i = 0; i < ACTION_VXLAN_ENCAP_ITEMS_NUM; i++) { > +        items = &item[i]; > +        switch (items->type) { > +        case RTE_FLOW_ITEM_TYPE_ETH: > +            item_eth = items->spec; > +            rte_memcpy(&dh_flow->encap0.dst_mac1, item_eth->dst.addr_bytes, 2); > +            rte_memcpy(&dh_flow->encap1.src_mac1, item_eth->src.addr_bytes, 2); > +            rte_memcpy(&dh_flow->encap0.dst_mac2, &item_eth->dst.addr_bytes[2], 4); > +            rte_memcpy(&dh_flow->encap1.src_mac2, &item_eth->src.addr_bytes[2], 4); > +            dh_flow->encap0.dst_mac1 = rte_bswap16(dh_flow->encap0.dst_mac1); > +            dh_flow->encap1.src_mac1 = rte_bswap16(dh_flow->encap1.src_mac1); > +            dh_flow->encap0.dst_mac2 = rte_bswap32(dh_flow->encap0.dst_mac2); > +            dh_flow->encap1.src_mac2 = rte_bswap32(dh_flow->encap1.src_mac2); > +            break; > +        case RTE_FLOW_ITEM_TYPE_VLAN: > +            item_vlan = items->spec; > +            dh_flow->encap1.vlan_tci = item_vlan->hdr.vlan_tci; > +            break; > +        case RTE_FLOW_ITEM_TYPE_IPV4: > +            item_ipv4 = items->spec; > +            dh_flow->encap0.ethtype = 0; > +            dh_flow->encap0.tos = item_ipv4->hdr.type_of_service; > +            dh_flow->encap0.ttl = item_ipv4->hdr.time_to_live; > +            addr = rte_bswap32(item_ipv4->hdr.src_addr); This byte-swaps the address here, but does not do it in 'fd_flow_parse_pattern', which again raises questions about endianness. This doesn't sit right with me. Perhaps I'm wrong and just missing something. May be add comments to explain. > +            rte_memcpy((uint32_t *)dh_flow->encap1.sip.ip_addr + 3, &addr, 4); > +            addr = rte_bswap32(item_ipv4->hdr.dst_addr); > +            rte_memcpy((uint32_t *)dh_flow->encap0.dip.ip_addr + 3, &addr, 4); > +            break; > +        case RTE_FLOW_ITEM_TYPE_IPV6: > +            item_ipv6 = items->spec; > +            dh_flow->encap0.ethtype = 1; > +            dh_flow->encap0.tos = > +                    (item_ipv6->hdr.vtc_flow & RTE_IPV6_HDR_TC_MASK) >> > +                        RTE_IPV6_HDR_TC_SHIFT; > +            dh_flow->encap0.ttl = item_ipv6->hdr.hop_limits; > +            rte_memcpy(dh_flow->encap1.sip.ip_addr, &item_ipv6->hdr.src_addr, 16); > +            dh_flow->encap1.sip.ip_addr[0] = > +                rte_bswap32(dh_flow->encap1.sip.ip_addr[0]); > +            dh_flow->encap1.sip.ip_addr[1] = > +                rte_bswap32(dh_flow->encap1.sip.ip_addr[1]); > +            dh_flow->encap1.sip.ip_addr[2] = > +                rte_bswap32(dh_flow->encap1.sip.ip_addr[2]); > +            dh_flow->encap1.sip.ip_addr[3] = > +                rte_bswap32(dh_flow->encap1.sip.ip_addr[3]); > +            rte_memcpy(dh_flow->encap0.dip.ip_addr, &item_ipv6->hdr.dst_addr, 16); > +            dh_flow->encap0.dip.ip_addr[0] = > +                    rte_bswap32(dh_flow->encap0.dip.ip_addr[0]); > +            dh_flow->encap0.dip.ip_addr[1] = > +                    rte_bswap32(dh_flow->encap0.dip.ip_addr[1]); > +            dh_flow->encap0.dip.ip_addr[2] = > +                    rte_bswap32(dh_flow->encap0.dip.ip_addr[2]); > +            dh_flow->encap0.dip.ip_addr[3] = > +                    rte_bswap32(dh_flow->encap0.dip.ip_addr[3]); > +            break; > +        case RTE_FLOW_ITEM_TYPE_UDP: > +            item_udp = items->spec; > +            dh_flow->encap0.tp_dst = item_udp->hdr.dst_port; > +            dh_flow->encap0.tp_dst = rte_bswap16(dh_flow->encap0.tp_dst); > +            break; > +        case RTE_FLOW_ITEM_TYPE_VXLAN: > +            item_vxlan = items->spec; > +            dh_flow->encap0.vni = item_vxlan->vni[0] * 65536 + > +                    item_vxlan->vni[1] * 256 + item_vxlan->vni[2]; > +            break; > +        case RTE_FLOW_ITEM_TYPE_END: VOID? > +        default: > +            break; > +        } > +    } > +    dh_flow->encap0.hit_flag = 1; > +    dh_flow->encap1.hit_flag = 1; > + > +    return 0; > +} > + > +static int > +fd_flow_parse_action(struct rte_eth_dev *dev, const struct rte_flow_action *actions, > +             struct rte_flow_error *error, struct zxdh_flow *dh_flow) > +{ > +    struct zxdh_flow_info *flow = NULL; > +    struct fd_flow_result *result = NULL; > +    const struct rte_flow_item *enc_item = NULL; > +    uint8_t action_bitmap = 0; > +    uint32_t dest_num = 0; > +    uint32_t mark_num = 0; > +    uint32_t counter_num = 0; > +    int ret; > + > +    rte_errno = 0; > +    if (dh_flow) { > +        flow = &dh_flow->flowentry; > +    } else { > +        flow = rte_zmalloc("dh_flow", sizeof(*flow), 0); > +        if (flow == NULL) { > +            rte_flow_error_set(error, EINVAL, > +                     RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                     "Failed to allocate memory "); > +            return -rte_errno; > +        } > +    } > +    result = &flow->fd_flow.result; > +    action_bitmap = result->action_idx; > + > +    for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) { > +        switch (actions->type) { > +        case RTE_FLOW_ACTION_TYPE_RSS: > +        { > +            dest_num++; > +            if (action_bitmap & (1 << FD_ACTION_RSS_BIT)) { > +                rte_flow_error_set(error, EINVAL, > +                        RTE_FLOW_ERROR_TYPE_ACTION, actions, > +                        "multi rss action no support."); This "multi" check repeats quite often. Perhaps factorise it somehow? > +                goto free_flow; > +            } > +            ret = validate_action_rss(dev, actions, error); > +            if (ret) > +                goto free_flow; > +            action_bitmap |= (1 << FD_ACTION_RSS_BIT); > +            break; > +        } > +        case RTE_FLOW_ACTION_TYPE_MARK: > +        { > +            mark_num++; > +            if (action_bitmap & (1 << FD_ACTION_MARK_BIT)) { > +                rte_flow_error_set(error, EINVAL, > +                        RTE_FLOW_ERROR_TYPE_ACTION, actions, > +                        "multi mark action no support."); > +                goto free_flow; > +            } > +            const struct rte_flow_action_mark *act_mark = actions->conf; > +            result->mark_fd_id = rte_cpu_to_le_32(act_mark->id); > +            action_bitmap |= (1 << FD_ACTION_MARK_BIT); > +            break; > +        } > +        case RTE_FLOW_ACTION_TYPE_COUNT: > +        { > +            counter_num++; > +            if (action_bitmap & (1 << FD_ACTION_COUNT_BIT)) { > +                rte_flow_error_set(error, EINVAL, > +                        RTE_FLOW_ERROR_TYPE_ACTION, actions, > +                        "multi count action no support."); > +                goto free_flow; > +            } > +            const struct rte_flow_action_count *act_count = actions->conf; > +            if (act_count->id > MAX_FLOW_COUNT_NUM) { > +                rte_flow_error_set(error, EINVAL, > +                            RTE_FLOW_ERROR_TYPE_ACTION, actions, > +                            "count action id no support."); > +                goto free_flow; > +            }; > +            result->countid = act_count->id; > +            action_bitmap |= (1 << FD_ACTION_COUNT_BIT); > +            break; > +        } > +        case RTE_FLOW_ACTION_TYPE_QUEUE: > +        { > +            dest_num++; > +            if (action_bitmap & (1 << FD_ACTION_QUEUE_BIT)) { > +                rte_flow_error_set(error, EINVAL, > +                        RTE_FLOW_ERROR_TYPE_ACTION, actions, > +                        "multi queue action no support."); > +                goto free_flow; > +            } > +            const struct rte_flow_action_queue *act_q; > +            act_q = actions->conf; > +            if (act_q->index >= dev->data->nb_rx_queues) { > +                rte_flow_error_set(error, EINVAL, > +                        RTE_FLOW_ERROR_TYPE_ACTION, actions, > +                        "Invalid queue ID"); > +                goto free_flow; > +            } > +            ret = zxdh_hw_qid_to_logic_qid(dev, act_q->index << 1); > +            if (ret < 0) { > +                rte_flow_error_set(error, EINVAL, > +                        RTE_FLOW_ERROR_TYPE_ACTION, actions, > +                        "Invalid phy queue ID ."); > +                goto free_flow; > +            } > +            result->qid = rte_cpu_to_le_16(ret); > +            action_bitmap |= (1 << FD_ACTION_QUEUE_BIT); > + > +            PMD_DRV_LOG(DEBUG, "QID RET 0x%x", result->qid); > +            break; > +        } > +        case RTE_FLOW_ACTION_TYPE_DROP: > +        { > +            dest_num++; > +            if (action_bitmap & (1 << FD_ACTION_DROP_BIT)) { > +                rte_flow_error_set(error, EINVAL, > +                        RTE_FLOW_ERROR_TYPE_ACTION, actions, > +                        "multi drop action no support."); > +                goto free_flow; > +            } > +            action_bitmap |= (1 << FD_ACTION_DROP_BIT); > +            break; > +        } > +        case RTE_FLOW_ACTION_TYPE_VXLAN_DECAP: > +        { > +            dest_num++; > +            if (action_bitmap & (1 << FD_ACTION_VXLAN_DECAP)) { > +                rte_flow_error_set(error, EINVAL, > +                        RTE_FLOW_ERROR_TYPE_ACTION, actions, > +                        "multi drop action no support."); > +                goto free_flow; > +            } > +            action_bitmap |= (1 << FD_ACTION_VXLAN_DECAP); > +            break; > +        } > +        case RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP: > +            enc_item = ((const struct rte_flow_action_vxlan_encap *) > +                   actions->conf)->definition; > +            if (dh_flow != NULL) > +                fd_flow_parse_vxlan_encap(dev, enc_item, dh_flow); > +            dest_num++; > +            if (action_bitmap & (1 << FD_ACTION_VXLAN_ENCAP)) { > +                rte_flow_error_set(error, EINVAL, > +                        RTE_FLOW_ERROR_TYPE_ACTION, actions, > +                        "multi drop action no support."); > +                goto free_flow; > +            } > +            action_bitmap |= (1 << FD_ACTION_VXLAN_ENCAP); > +            break; > +        default: > +            rte_flow_error_set(error, EINVAL, > +                RTE_FLOW_ERROR_TYPE_ACTION, actions, > +                "Invalid action."); > +            goto free_flow; > +        } > +    } > + > +    if (dest_num >= 2) { > +        rte_flow_error_set(error, EINVAL, > +               RTE_FLOW_ERROR_TYPE_ACTION, actions, > +               "Unsupported action combination"); > +        return -rte_errno; > +    } > + > +    if (mark_num >= 2) { But hasn't this already been checked by above 'multi' checks? > +        rte_flow_error_set(error, EINVAL, > +               RTE_FLOW_ERROR_TYPE_ACTION, actions, > +               "Too many mark actions"); > +        return -rte_errno; > +    } > + > +    if (counter_num >= 2) { > +        rte_flow_error_set(error, EINVAL, > +               RTE_FLOW_ERROR_TYPE_ACTION, actions, > +               "Too many count actions"); > +        return -rte_errno; > +    } > + > +    if (dest_num + mark_num + counter_num == 0) { > +        rte_flow_error_set(error, EINVAL, > +               RTE_FLOW_ERROR_TYPE_ACTION, actions, > +               "Empty action"); From this it follows that if the user does not specify any "fate" action, say, DROP or QUEUE, then the HW will simply forward the packet to its originally envisaged destination. This is OK, but may be describe this allowed behaviour in comments and/or documentation. > +        return -rte_errno; > +    } > + > +    result->action_idx = action_bitmap; > +    return 0; > + > +free_flow: > +    if (!dh_flow) > +        rte_free(flow); > +    return -rte_errno; > +} > + > +static int > +fd_parse_pattern_action(struct rte_eth_dev *dev, > +            const struct rte_flow_attr *attr, > +            const struct rte_flow_item pattern[], > +            const struct rte_flow_action *actions, > +            struct rte_flow_error *error, struct zxdh_flow *dh_flow) > +{ > +    int ret = 0; > +    ret = fd_flow_parse_attr(dev, attr, error, dh_flow); > +    if (ret < 0) > +        return -rte_errno; > +    ret = fd_flow_parse_pattern(dev, pattern, error, dh_flow); > +    if (ret < 0) > +        return -rte_errno; > + > +    ret = fd_flow_parse_action(dev, actions, error, dh_flow); > +    if (ret < 0) > +        return -rte_errno; > +    return 0; > +} > + > +struct dh_flow_engine pf_fd_engine = { > +    .apply = pf_fd_hw_apply, > +    .destroy = pf_fd_hw_destroy, > +    .query_count = pf_fd_hw_query_count, > +    .parse_pattern_action = fd_parse_pattern_action, > +    .type = FLOW_TYPE_FD_TCAM, > +}; > + > + > +static int > +vf_flow_msg_process(enum zxdh_msg_type msg_type, struct rte_eth_dev *dev, > +        struct zxdh_flow *dh_flow, struct rte_flow_error *error, > +        struct rte_flow_query_count *count) > +{ > +    int ret = 0; > +    struct zxdh_hw *hw = dev->data->dev_private; > +    struct zxdh_msg_info msg_info = {0}; > +    struct zxdh_flow_op_msg *flow_msg = &msg_info.data.flow_msg; > + > +    uint8_t zxdh_msg_reply_info[ZXDH_ST_SZ_BYTES(msg_reply_info)] = {0}; > +    void *reply_body_addr = ZXDH_ADDR_OF(msg_reply_info, zxdh_msg_reply_info, reply_body); > +    void *flow_rsp_addr = ZXDH_ADDR_OF(msg_reply_body, reply_body_addr, flow_rsp); > +    uint8_t flow_op_rsp[sizeof(struct zxdh_flow_op_rsp)] = {0}; > +    uint16_t len = sizeof(struct zxdh_flow_op_rsp) - 4; > +    struct zxdh_flow_op_rsp *flow_rsp = (struct zxdh_flow_op_rsp *)flow_op_rsp; > + > +    dh_flow->hash_search_index = hw->hash_search_index; > +    rte_memcpy(&flow_msg->dh_flow, dh_flow, sizeof(struct zxdh_flow)); > + > +    zxdh_msg_head_build(hw, msg_type, &msg_info); > +    ret = zxdh_vf_send_msg_to_pf(dev, &msg_info, sizeof(struct zxdh_msg_info), > +            (void *)zxdh_msg_reply_info, ZXDH_ST_SZ_BYTES(msg_reply_info)); > +    zxdh_adjust_flow_op_rsp_memory_layout(flow_rsp_addr, len, flow_op_rsp); > +    if (ret) { > +        PMD_DRV_LOG(ERR, "port %d flow op %d failed ret %d", hw->port_id, msg_type, ret); > +        if (ret == -2) { > +            PMD_DRV_LOG(ERR, "port %d  flow %d failed: cause %s", > +                 hw->port_id, msg_type, flow_rsp->error.reason); > +            rte_flow_error_set(error, EBUSY, > +                     RTE_FLOW_ERROR_TYPE_HANDLE, NULL, > +                     flow_rsp->error.reason); > +        } else { > +            rte_flow_error_set(error, EBUSY, > +                     RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, > +                     "msg channel error"); > +        } > +        return ret; > +    } > + > +    if (msg_type == ZXDH_FLOW_HW_ADD) > +        dh_flow->flowentry.hw_idx = flow_rsp->dh_flow.flowentry.hw_idx; > +    if (count) > +        rte_memcpy((void *)count, &flow_rsp->count, sizeof(flow_rsp->count)); > + > +    return ret; > +} > + > +static int > +vf_fd_apply(struct rte_eth_dev *dev, struct zxdh_flow *dh_flow, > +        struct rte_flow_error *error, uint16_t vport __rte_unused, > +        uint16_t pcieid __rte_unused) > +{ > +    int ret = 0; > +    struct zxdh_hw *hw = dev->data->dev_private; > +    ret =  vf_flow_msg_process(ZXDH_FLOW_HW_ADD, dev, dh_flow, error, NULL); > +    if (!ret) { > +        uint8_t action_bits = dh_flow->flowentry.fd_flow.result.action_idx; > +        if (((action_bits & (1 << FD_ACTION_VXLAN_ENCAP)) != 0) || > +                ((action_bits & (1 << FD_ACTION_VXLAN_DECAP)) != 0)) { > +            hw->vxlan_fd_num++; > +            if (hw->vxlan_fd_num == 1) { > +                set_vxlan_enable(dev, 1, error); > +                PMD_DRV_LOG(DEBUG, "vf set_vxlan_enable"); > +            } > +        } > +    } > +    return ret; > +} > + > +static int > +vf_fd_destroy(struct rte_eth_dev *dev, struct zxdh_flow *dh_flow, > +        struct rte_flow_error *error, uint16_t vport __rte_unused, > +        uint16_t pcieid __rte_unused) > +{ > +    int ret = 0; > +    struct zxdh_hw *hw = dev->data->dev_private; > +    ret = vf_flow_msg_process(ZXDH_FLOW_HW_DEL, dev, dh_flow, error, NULL); > +    if (!ret) { > +        uint8_t action_bits = dh_flow->flowentry.fd_flow.result.action_idx; > +        if (((action_bits & (1 << FD_ACTION_VXLAN_ENCAP)) != 0) || > +                ((action_bits & (1 << FD_ACTION_VXLAN_DECAP)) != 0)) { > +            hw->vxlan_fd_num--; > +            if (hw->vxlan_fd_num == 0) { > +                set_vxlan_enable(dev, 0, error); > +                PMD_DRV_LOG(DEBUG, "vf set_vxlan_disable"); > +            } > +        } > +    } > +    return ret; > +} > + > +static int > +vf_fd_query_count(struct rte_eth_dev *dev, > +        struct zxdh_flow *dh_flow, > +        struct rte_flow_query_count *count, > +        struct rte_flow_error *error) > +{ > +    int ret = 0; > +    ret = vf_flow_msg_process(ZXDH_FLOW_HW_GET, dev, dh_flow, error, count); > +    return ret; > +} > + > + > +static struct dh_flow_engine vf_fd_engine = { > +    .apply = vf_fd_apply, > +    .destroy = vf_fd_destroy, > +    .parse_pattern_action = fd_parse_pattern_action, > +    .query_count = vf_fd_query_count, > +    .type = FLOW_TYPE_FD_TCAM, > +}; > + > +void zxdh_flow_init(struct rte_eth_dev *dev) > +{ > +    struct zxdh_hw *priv =  dev->data->dev_private; > +    if (priv->is_pf) > +        zxdh_register_flow_engine(&pf_fd_engine); > +    else > +        zxdh_register_flow_engine(&vf_fd_engine); > +    TAILQ_INIT(&priv->dh_flow_list); > +} > + > +const struct rte_flow_ops zxdh_flow_ops = { > +    .validate = zxdh_flow_validate, > +    .create = zxdh_flow_create, > +    .destroy = zxdh_flow_destroy, > +    .flush = zxdh_flow_flush, > +    .query = zxdh_flow_query, > +    .dev_dump = zxdh_flow_dev_dump, > +}; > + > +int > +zxdh_flow_ops_get(struct rte_eth_dev *dev __rte_unused, > +        const struct rte_flow_ops **ops) > +{ > +    *ops = &zxdh_flow_ops; > + > +    return 0; > +} > + > +void > +zxdh_flow_release(struct rte_eth_dev *dev) > +{ > +    struct rte_flow_error error = {0}; > +    const struct rte_flow_ops *flow_ops = NULL; > + > +    if (dev->dev_ops && dev->dev_ops->flow_ops_get) > +        dev->dev_ops->flow_ops_get(dev, &flow_ops); > +    if (flow_ops && flow_ops->flush) > +        flow_ops->flush(dev, &error); > +} > diff --git a/drivers/net/zxdh/zxdh_flow.h b/drivers/net/zxdh/zxdh_flow.h > new file mode 100644 > index 0000000000..cbcf71b3e1 > --- /dev/null > +++ b/drivers/net/zxdh/zxdh_flow.h > @@ -0,0 +1,237 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright(c) 2024 ZTE Corporation Why 2024? Thank you. > + */ > + > +#ifndef ZXDH_FLOW_H > +#define ZXDH_FLOW_H > + > +#include  > +#include  > +#include  > + > +#include  > +#include  > +#include  > +#include  > +#include  > +#include  > +#include  > +#include  > +#include  > +#include  > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +#define MAX_GROUP                  1 > +#define ZXDH_MAX_FLOW_NUM          2048 > +#define MAX_FLOW_COUNT_NUM         ZXDH_MAX_FLOW_NUM > +#define ZXDH_FLOW_GROUP_TCAM       1 > + > +#ifndef IPv4_BYTES > +#define IPv4_BYTES_FMT "%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 > +#define IPv4_BYTES(addr) \ > +        (uint8_t)(((addr) >> 24) & 0xFF),\ > +        (uint8_t)(((addr) >> 16) & 0xFF),\ > +        (uint8_t)(((addr) >> 8) & 0xFF),\ > +        (uint8_t)((addr) & 0xFF) > +#endif > + > +#ifndef IPv6_BYTES > +#define IPv6_BYTES_FMT "%02x%02x:%02x%02x:%02x%02x:%02x%02x:" \ > +                        "%02x%02x:%02x%02x:%02x%02x:%02x%02x" > +#define IPv6_BYTES(addr) \ > +    addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7], \ > +    addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15] > +#endif > + > +enum { > +    FD_ACTION_VXLAN_ENCAP = 0, > +    FD_ACTION_VXLAN_DECAP = 1, > +    FD_ACTION_RSS_BIT = 2, > +    FD_ACTION_COUNT_BIT = 3, > +    FD_ACTION_DROP_BIT = 4, > +    FD_ACTION_MARK_BIT = 5, > +    FD_ACTION_QUEUE_BIT = 6, > +}; > + > +struct fd_flow_key { Endianness remarks would be helpful. > +    struct rte_ether_addr mac_dst; /**< Destination MAC. */ > +    struct rte_ether_addr mac_src; /**< Source MAC. */ > +    rte_be16_t ether_type; /**< EtherType  */ > +    union { > +        struct { > +            rte_be16_t cvlan_pri:4; /**< vlanid 0xfff  is  valid */ > +            rte_be16_t cvlan_vlanid:12; /**< vlanid 0xfff  is  valid */ > +        }; > +        rte_be16_t  vlan_tci; > +    }; > + > +    uint8_t  src_ip[16];  /** ip src  */ > +    uint8_t  dst_ip[16];  /** ip dst  */ > +    uint8_t  rsv0; > +    union { > +        uint8_t  tos; > +        uint8_t  tc; > +    }; > +    uint8_t  nw_proto; > +    uint8_t  frag_flag;/*1表示分片 0 表示非分片*/ > +    rte_be16_t  tp_src; > +    rte_be16_t  tp_dst; > + > +    uint8_t rsv1;/**/ > +    uint8_t vni[3];/**/ > + > +    rte_be16_t vfid; > +    uint8_t rsv2[18]; > +}; > + > +struct fd_flow_result { > +    rte_le16_t qid; > +    uint8_t rsv0; > + > +    uint8_t action_idx:7; > +    uint8_t hit_flag:1; > + > +    rte_le32_t mark_fd_id; > +    rte_le32_t countid:20; > +    rte_le32_t encap1_index:12; > + > +    rte_le16_t encap0_index:12; > +    rte_le16_t rsv1:4; > +    uint8_t rss_hash_factor; > +    uint8_t rss_hash_alg; > +}; > + > +struct fd_flow_entry { > +    struct fd_flow_key key; > +    struct fd_flow_key key_mask; > +    struct fd_flow_result result; > +}; > + > +struct flow_stats { > +    uint32_t hit_pkts_hi; > +    uint32_t hit_pkts_lo; > +    uint32_t hit_bytes_hi; > +    uint32_t hit_bytes_lo; > +}; > + > + > +enum dh_flow_type { > +     FLOW_TYPE_FLOW = 0, > +     FLOW_TYPE_FD_TCAM, > +     FLOW_TYPE_FD_SW, > +}; > + > +struct zxdh_flow_info { > +    enum dh_flow_type flowtype; > +    uint16_t hw_idx; > +    uint16_t rsv; > +    union { > +        struct fd_flow_entry fd_flow; > +    }; > +}; > + > +struct tunnel_encap_ip { > +    rte_be32_t ip_addr[4]; > +}; > + > +struct tunnel_encap0 { > +    uint8_t tos; > +    uint8_t rsv2[2]; > +    uint8_t rsv1: 6; > +    uint8_t ethtype: 1; > +    uint8_t hit_flag: 1; > +    uint16_t dst_mac1; > +    uint16_t tp_dst; > +    uint32_t dst_mac2; > +    uint32_t ttl:8; > +    uint32_t vni:24; > +    struct tunnel_encap_ip dip; > +}; > + > +struct tunnel_encap1 { > +    uint32_t rsv1: 31; > +    uint32_t hit_flag: 1; > +    uint16_t src_mac1; > +    uint16_t vlan_tci; > +    uint32_t src_mac2; > +    uint32_t rsv; > +    struct tunnel_encap_ip sip; > +}; > + > +struct zxdh_flow { > +    uint8_t direct; /* 0 in 1 out */ > +    uint8_t group;  /* rule group id */ > +    uint8_t pri; /* priority */ > +    uint8_t hash_search_index; /*  */ > +    struct zxdh_flow_info  flowentry; > +    struct tunnel_encap0  encap0; > +    struct tunnel_encap1  encap1; > +}; > +TAILQ_HEAD(dh_flow_list, rte_flow); > + > +struct rte_flow { > +    TAILQ_ENTRY(rte_flow) next; > +    void *driver_flow; > +    uint32_t type; > +    uint16_t port_id; > +}; > + > +struct count_res { > +    rte_spinlock_t count_lock; > +    uint8_t count_ref; > +    uint8_t rev[3]; > +}; > + > +/* Struct to store engine created. */ > +struct dh_flow_engine { > +    TAILQ_ENTRY(dh_flow_engine) node; > +    enum dh_flow_type type; > +    int (*apply) > +        (struct rte_eth_dev *dev, > +         struct zxdh_flow *dh_flow, > +         struct rte_flow_error *error, > +         uint16_t vport, uint16_t pcieid); > + > +    int (*parse_pattern_action) > +        (struct rte_eth_dev *dev, > +         const struct rte_flow_attr *attr, > +         const struct rte_flow_item pattern[], > +         const struct rte_flow_action *actions, > +         struct rte_flow_error *error, > +         struct zxdh_flow *dh_flow); > + > +    int (*destroy) > +        (struct rte_eth_dev *dev, > +         struct zxdh_flow *dh_flow, > +         struct rte_flow_error *error, > +         uint16_t vport, uint16_t pcieid); > + > +    int (*query_count) > +        (struct rte_eth_dev *dev, > +         struct zxdh_flow *dh_flow, > +         struct rte_flow_query_count *count, > +         struct rte_flow_error *error); > +}; > +TAILQ_HEAD(dh_engine_list, dh_flow_engine); > + > +void zxdh_register_flow_engine(struct dh_flow_engine *engine); > + > +extern const struct rte_flow_ops zxdh_flow_ops; > + > +void zxdh_flow_global_init(void); > +void zxdh_flow_init(struct rte_eth_dev *dev); > +int pf_fd_hw_apply(struct rte_eth_dev *dev, struct zxdh_flow *dh_flow, > +                 struct rte_flow_error *error, uint16_t vport, uint16_t pcieid); > +int pf_fd_hw_destroy(struct rte_eth_dev *dev, struct zxdh_flow *dh_flow, > +                 struct rte_flow_error *error, uint16_t vport, uint16_t pcieid); > +int pf_fd_hw_query_count(struct rte_eth_dev *dev, > +                        struct zxdh_flow *flow, > +                        struct rte_flow_query_count *count, > +                        struct rte_flow_error *error); > +int zxdh_flow_ops_get(struct rte_eth_dev *dev, const struct rte_flow_ops **ops); > +void zxdh_flow_release(struct rte_eth_dev *dev); > + > +#endif /* ZXDH_FLOW_H */ > diff --git a/drivers/net/zxdh/zxdh_msg.c b/drivers/net/zxdh/zxdh_msg.c > index 980509500f..196e27f91c 100644 > --- a/drivers/net/zxdh/zxdh_msg.c > +++ b/drivers/net/zxdh/zxdh_msg.c > @@ -19,6 +19,7 @@ >  #include "zxdh_tables.h" >  #include "zxdh_np.h" >  #include "zxdh_common.h" > +#include "zxdh_flow.h" >   >  #define ZXDH_REPS_INFO_FLAG_USABLE  0x00 >  #define ZXDH_BAR_SEQID_NUM_MAX      256 > @@ -1296,7 +1297,8 @@ zxdh_vf_vlan_table_init(struct zxdh_hw *hw, uint16_t vport) >  } >   >  static int > -zxdh_vf_port_init(struct zxdh_hw *pf_hw, uint16_t vport, void *cfg_data, > +zxdh_vf_port_init(struct zxdh_hw *pf_hw, uint16_t vport, > +        uint16_t pcieid, void *cfg_data, >          void *res_info, uint16_t *res_len) >  { >      struct zxdh_port_attr_table port_attr = {0}; > @@ -1314,7 +1316,8 @@ zxdh_vf_port_init(struct zxdh_hw *pf_hw, uint16_t vport, void *cfg_data, >      port_attr.pf_vfid = pf_hw->vfid; >      port_attr.hash_search_index = pf_hw->hash_search_index; >      port_attr.port_base_qid = vf_init_msg->base_qid; > - > +    int vf_index = VF_IDX(pcieid); > +    pf_hw->vfinfo[vf_index].vport = vport; >      ret = zxdh_set_port_attr(pf_hw, vport, &port_attr); >      if (ret) { >          PMD_DRV_LOG(ERR, "set vport attr failed, code:%d", ret); > @@ -1333,6 +1336,11 @@ zxdh_vf_port_init(struct zxdh_hw *pf_hw, uint16_t vport, void *cfg_data, >          goto proc_end; >      } >   > +    ret = zxdh_np_dtb_acl_offline_delete(pf_hw->dev_id, pf_hw->dev_sd->dtb_sd.queueid, > +                ZXDH_SDT_FD_TABLE, vport, ZXDH_FLOW_STATS_INGRESS_BASE, 1); > +    if (ret) > +        PMD_DRV_LOG(ERR, "flow table delete failed. code:%d", ret); > + >      ZXDH_SET(msg_reply_body, res_info, flag, ZXDH_REPS_SUCC); >      *res_len = sizeof(uint8_t); >   > @@ -1344,30 +1352,30 @@ zxdh_vf_port_init(struct zxdh_hw *pf_hw, uint16_t vport, void *cfg_data, >  } >   >  static int > -zxdh_mac_clear(struct zxdh_hw *hw, union zxdh_virport_num vport) > +zxdh_mac_clear(struct zxdh_hw *hw, union zxdh_virport_num vport, uint16_t pcieid) >  { > -    uint16_t vf_id = vport.vfid; > +    uint16_t vf_index = VF_IDX(pcieid); >      int i; >      int ret = 0; >   >      for (i = 0; (i != ZXDH_MAX_MAC_ADDRS); ++i) { > -        if (!rte_is_zero_ether_addr(&hw->vfinfo[vf_id].vf_mac[i])) { > +        if (!rte_is_zero_ether_addr(&hw->vfinfo[vf_index].vf_mac[i])) { >              ret = zxdh_del_mac_table(hw, vport.vport, > -                    &hw->vfinfo[vf_id].vf_mac[i], > +                    &hw->vfinfo[vf_index].vf_mac[i], >                      hw->hash_search_index, 0, 0); >              if (ret) { >                  PMD_DRV_LOG(ERR, "vf_del_mac_failed. code:%d", ret); >                  return ret; >              } > -            memset(&hw->vfinfo[vf_id].vf_mac[i], 0, sizeof(struct rte_ether_addr)); > +            memset(&hw->vfinfo[vf_index].vf_mac[i], 0, sizeof(struct rte_ether_addr)); >          } >      } >      return ret; >  } >   >  static int > -zxdh_vf_port_uninit(struct zxdh_hw *pf_hw, > -        uint16_t vport, void *cfg_data __rte_unused, > +zxdh_vf_port_uninit(struct zxdh_hw *pf_hw, uint16_t vport, > +        uint16_t pcieid, void *cfg_data __rte_unused, >          void *res_info, uint16_t *res_len) >  { >      char str[ZXDH_MSG_REPLY_BODY_MAX_LEN] = "uninit"; > @@ -1385,7 +1393,7 @@ zxdh_vf_port_uninit(struct zxdh_hw *pf_hw, >          goto proc_end; >      } >   > -    ret = zxdh_mac_clear(pf_hw, vport_num); > +    ret = zxdh_mac_clear(pf_hw, vport_num, pcieid); >      if (ret) { >          PMD_DRV_LOG(ERR, "zxdh_mac_clear failed, code:%d", ret); >          goto proc_end; > @@ -1410,7 +1418,8 @@ zxdh_vf_port_uninit(struct zxdh_hw *pf_hw, >  } >   >  static int > -zxdh_add_vf_mac_table(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, > +zxdh_add_vf_mac_table(struct zxdh_hw *hw, uint16_t vport, > +        uint16_t pcieid, void *cfg_data, >          void *reply_body, uint16_t *reply_len) >  { >      char str[ZXDH_MSG_REPLY_BODY_MAX_LEN] = "add mac"; > @@ -1419,12 +1428,12 @@ zxdh_add_vf_mac_table(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, >      struct rte_ether_addr *addr = &mac_filter->mac; >      void *reply_data_addr = ZXDH_ADDR_OF(msg_reply_body, reply_body, reply_data); >      void *mac_reply_msg_addr = ZXDH_ADDR_OF(msg_reply_body, reply_body, mac_reply_msg); > +    uint16_t vf_index = VF_IDX(pcieid); >      port.vport = vport; > -    uint16_t vf_id = port.vfid; >      int i = 0, ret = 0; >   >      for (i = 0; i < ZXDH_MAX_MAC_ADDRS; i++) > -        if (rte_is_same_ether_addr(&hw->vfinfo[vf_id].vf_mac[i], addr)) > +        if (rte_is_same_ether_addr(&hw->vfinfo[vf_index].vf_mac[i], addr)) >              goto success; >   >      ret = zxdh_add_mac_table(hw, vport, addr, hw->hash_search_index, 0, 0); > @@ -1440,8 +1449,8 @@ zxdh_add_vf_mac_table(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, >          goto failure; >      } >      for (i = 0; i < ZXDH_MAX_MAC_ADDRS; i++) { > -        if (rte_is_zero_ether_addr(&hw->vfinfo[vf_id].vf_mac[i])) { > -            memcpy(&hw->vfinfo[vf_id].vf_mac[i], addr, 6); > +        if (rte_is_zero_ether_addr(&hw->vfinfo[vf_index].vf_mac[i])) { > +            memcpy(&hw->vfinfo[vf_index].vf_mac[i], addr, 6); >              break; >          } >      } > @@ -1461,14 +1470,15 @@ zxdh_add_vf_mac_table(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, >  } >   >  static int > -zxdh_del_vf_mac_table(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, > -    void *res_info, uint16_t *res_len) > +zxdh_del_vf_mac_table(struct zxdh_hw *hw, uint16_t vport, > +        uint16_t pcieid, void *cfg_data, > +        void *res_info, uint16_t *res_len) >  { >      struct zxdh_mac_filter *mac_filter = (struct zxdh_mac_filter *)cfg_data; >      union zxdh_virport_num  port = (union zxdh_virport_num)vport; >      char str[ZXDH_MSG_REPLY_BODY_MAX_LEN] = "del mac"; >      void *reply_data_addr = ZXDH_ADDR_OF(msg_reply_body, res_info, reply_data); > -    uint16_t vf_id = port.vfid; > +    uint16_t vf_index = VF_IDX(pcieid); >      int ret, i = 0; >   >      PMD_DRV_LOG(DEBUG, "[PF GET MSG FROM VF]--vf mac to del."); > @@ -1483,8 +1493,8 @@ zxdh_del_vf_mac_table(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, >      } >   >      for (i = 0; i < ZXDH_MAX_MAC_ADDRS; i++) { > -        if (rte_is_same_ether_addr(&hw->vfinfo[vf_id].vf_mac[i], &mac_filter->mac)) > -            memset(&hw->vfinfo[vf_id].vf_mac[i], 0, sizeof(struct rte_ether_addr)); > +        if (rte_is_same_ether_addr(&hw->vfinfo[vf_index].vf_mac[i], &mac_filter->mac)) > +            memset(&hw->vfinfo[vf_index].vf_mac[i], 0, sizeof(struct rte_ether_addr)); >      } >   >      sprintf(str, "vport 0x%x del mac ret 0x%x\n", port.vport, ret); > @@ -1500,7 +1510,8 @@ zxdh_del_vf_mac_table(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, >  } >   >  static int > -zxdh_vf_promisc_set(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, > +zxdh_vf_promisc_set(struct zxdh_hw *hw, uint16_t vport, > +        uint16_t pcieid __rte_unused, void *cfg_data, >          void *reply, uint16_t *res_len) >  { >      struct zxdh_port_promisc_msg *promisc_msg = (struct zxdh_port_promisc_msg *)cfg_data; > @@ -1531,7 +1542,8 @@ zxdh_vf_promisc_set(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, >  } >   >  static int > -zxdh_vf_vlan_filter_table_process(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, > +zxdh_vf_vlan_filter_table_process(struct zxdh_hw *hw, uint16_t vport, > +        uint16_t pcieid __rte_unused, void *cfg_data, >          void *res_info, uint16_t *res_len, uint8_t enable) >  { >      struct zxdh_vlan_filter *vlan_filter = cfg_data; > @@ -1556,21 +1568,24 @@ zxdh_vf_vlan_filter_table_process(struct zxdh_hw *hw, uint16_t vport, void *cfg_ >  } >   >  static int > -zxdh_vf_vlan_filter_table_add(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, > +zxdh_vf_vlan_filter_table_add(struct zxdh_hw *hw, uint16_t vport, > +        uint16_t pcieid, void *cfg_data, >          void *res_info, uint16_t *res_len) >  { > -    return zxdh_vf_vlan_filter_table_process(hw, vport, cfg_data, res_info, res_len, 1); > +    return zxdh_vf_vlan_filter_table_process(hw, vport, pcieid, cfg_data, res_info, res_len, 1); >  } >   >  static int > -zxdh_vf_vlan_filter_table_del(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, > +zxdh_vf_vlan_filter_table_del(struct zxdh_hw *hw, uint16_t vport, > +        uint16_t pcieid, void *cfg_data, >          void *res_info, uint16_t *res_len) >  { > -    return zxdh_vf_vlan_filter_table_process(hw, vport, cfg_data, res_info, res_len, 0); > +    return zxdh_vf_vlan_filter_table_process(hw, vport, pcieid, cfg_data, res_info, res_len, 0); >  } >   >  static int > -zxdh_vf_set_vlan_filter(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, > +zxdh_vf_set_vlan_filter(struct zxdh_hw *hw, uint16_t vport, > +        uint16_t pcieid __rte_unused, void *cfg_data, >          void *reply, uint16_t *res_len) >  { >      struct zxdh_vlan_filter_set *vlan_filter = cfg_data; > @@ -1594,7 +1609,8 @@ zxdh_vf_set_vlan_filter(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, >  } >   >  static int > -zxdh_vf_set_vlan_offload(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, > +zxdh_vf_set_vlan_offload(struct zxdh_hw *hw, uint16_t vport, > +        uint16_t pcieid __rte_unused, void *cfg_data, >          void *reply, uint16_t *res_len) >  { >      struct zxdh_vlan_offload *vlan_offload = cfg_data; > @@ -1621,8 +1637,9 @@ zxdh_vf_set_vlan_offload(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, >  } >   >  static int > -zxdh_vf_rss_hf_get(struct zxdh_hw *hw, uint16_t vport, void *cfg_data __rte_unused, > -            void *reply, uint16_t *res_len) > +zxdh_vf_rss_hf_get(struct zxdh_hw *hw, uint16_t vport, > +        uint16_t pcieid __rte_unused, void *cfg_data __rte_unused, > +        void *reply, uint16_t *res_len) >  { >      char str[ZXDH_MSG_REPLY_BODY_MAX_LEN] = "rss_hf"; >      struct zxdh_port_attr_table vport_att = {0}; > @@ -1650,8 +1667,9 @@ zxdh_vf_rss_hf_get(struct zxdh_hw *hw, uint16_t vport, void *cfg_data __rte_unus >  } >   >  static int > -zxdh_vf_rss_hf_set(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, > -            void *reply, uint16_t *res_len) > +zxdh_vf_rss_hf_set(struct zxdh_hw *hw, uint16_t vport, > +        uint16_t pcieid __rte_unused, void *cfg_data, > +        void *reply, uint16_t *res_len) >  { >      char str[ZXDH_MSG_REPLY_BODY_MAX_LEN] = "rss_hf"; >      struct zxdh_rss_hf *rss_hf = cfg_data; > @@ -1686,8 +1704,9 @@ zxdh_vf_rss_hf_set(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, >  } >   >  static int > -zxdh_vf_rss_enable(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, > -            void *reply, uint16_t *res_len) > +zxdh_vf_rss_enable(struct zxdh_hw *hw, uint16_t vport, > +        uint16_t pcieid __rte_unused, void *cfg_data, > +        void *reply, uint16_t *res_len) >  { >      char str[ZXDH_MSG_REPLY_BODY_MAX_LEN] = "rss_enable"; >      struct zxdh_rss_enable *rss_enable = cfg_data; > @@ -1722,7 +1741,8 @@ zxdh_vf_rss_enable(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, >  } >   >  static int > -zxdh_vf_rss_table_set(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, > +zxdh_vf_rss_table_set(struct zxdh_hw *hw, uint16_t vport, > +        uint16_t pcieid __rte_unused, void *cfg_data, >          void *reply, uint16_t *res_len) >  { >      char str[ZXDH_MSG_REPLY_BODY_MAX_LEN] = "rss_table"; > @@ -1744,7 +1764,8 @@ zxdh_vf_rss_table_set(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, >  } >   >  static int > -zxdh_vf_rss_table_get(struct zxdh_hw *hw, uint16_t vport, void *cfg_data __rte_unused, > +zxdh_vf_rss_table_get(struct zxdh_hw *hw, uint16_t vport, > +        uint16_t pcieid __rte_unused, void *cfg_data __rte_unused, >          void *reply, uint16_t *res_len) >  { >      char str[ZXDH_MSG_REPLY_BODY_MAX_LEN] = "rss_table"; > @@ -1770,8 +1791,9 @@ zxdh_vf_rss_table_get(struct zxdh_hw *hw, uint16_t vport, void *cfg_data __rte_u >  } >   >  static int > -zxdh_vf_port_attr_set(struct zxdh_hw *pf_hw, uint16_t vport, void *cfg_data, > -    void *res_info, uint16_t *res_len) > +zxdh_vf_port_attr_set(struct zxdh_hw *pf_hw, uint16_t vport, > +        uint16_t pcieid __rte_unused, void *cfg_data, > +        void *res_info, uint16_t *res_len) >  { >      RTE_ASSERT(!cfg_data || !pf_hw); >      if (res_info) > @@ -1833,8 +1855,8 @@ zxdh_vf_port_attr_set(struct zxdh_hw *pf_hw, uint16_t vport, void *cfg_data, >   >  static int >  zxdh_vf_np_stats_update(struct zxdh_hw *pf_hw, uint16_t vport, > -        void *cfg_data, void *res_info, > -        uint16_t *res_len) > +        uint16_t pcieid __rte_unused, void *cfg_data, > +        void *res_info, uint16_t *res_len) >  { >      struct zxdh_np_stats_updata_msg *np_stats_query = >               (struct zxdh_np_stats_updata_msg  *)cfg_data; > @@ -2015,10 +2037,9 @@ zxdh_vf_np_stats_update(struct zxdh_hw *pf_hw, uint16_t vport, >  } >   >  static int > -zxdh_vf_mtr_hw_stats_get(struct zxdh_hw *pf_hw, > -    uint16_t vport, void *cfg_data, > -    void *res_info, > -    uint16_t *res_len) > +zxdh_vf_mtr_hw_stats_get(struct zxdh_hw *pf_hw, uint16_t vport, > +        uint16_t pcieid __rte_unused, void *cfg_data, > +        void *res_info, uint16_t *res_len) >  { >      struct zxdh_mtr_stats_query  *zxdh_mtr_stats_query = >              (struct zxdh_mtr_stats_query  *)cfg_data; > @@ -2048,11 +2069,9 @@ zxdh_vf_mtr_hw_stats_get(struct zxdh_hw *pf_hw, >  } >   >  static int > -zxdh_vf_mtr_hw_profile_add(struct zxdh_hw *pf_hw, > -    uint16_t vport, > -    void *cfg_data, > -    void *res_info, > -    uint16_t *res_len) > +zxdh_vf_mtr_hw_profile_add(struct zxdh_hw *pf_hw, uint16_t vport, > +        uint16_t pcieid __rte_unused, void *cfg_data, > +        void *res_info, uint16_t *res_len) >  { >      if (!cfg_data || !res_len || !res_info) { >          PMD_DRV_LOG(ERR, " get profileid invalid inparams"); > @@ -2088,11 +2107,9 @@ zxdh_vf_mtr_hw_profile_add(struct zxdh_hw *pf_hw, >  } >   >  static int > -zxdh_vf_mtr_hw_profile_del(struct zxdh_hw *pf_hw, > -    uint16_t vport, > -    void *cfg_data, > -    void *res_info, > -    uint16_t *res_len) > +zxdh_vf_mtr_hw_profile_del(struct zxdh_hw *pf_hw, uint16_t vport, > +        uint16_t pcieid __rte_unused, void *cfg_data, > +        void *res_info, uint16_t *res_len) >  { >      if (!cfg_data || !res_len || !res_info) { >          PMD_DRV_LOG(ERR, " del profileid  invalid inparams"); > @@ -2130,11 +2147,9 @@ zxdh_vf_mtr_hw_profile_del(struct zxdh_hw *pf_hw, >  } >   >  static int > -zxdh_vf_mtr_hw_plcrflow_cfg(struct zxdh_hw *pf_hw, > -    uint16_t vport, > -    void *cfg_data, > -    void *res_info, > -    uint16_t *res_len) > +zxdh_vf_mtr_hw_plcrflow_cfg(struct zxdh_hw *pf_hw, uint16_t vport, > +        uint16_t pcieid __rte_unused, void *cfg_data, > +        void *res_info, uint16_t *res_len) >  { >      int ret = 0; >   > @@ -2169,11 +2184,9 @@ zxdh_vf_mtr_hw_plcrflow_cfg(struct zxdh_hw *pf_hw, >  } >   >  static int > -zxdh_vf_mtr_hw_profile_cfg(struct zxdh_hw *pf_hw __rte_unused, > -    uint16_t vport, > -    void *cfg_data, > -    void *res_info, > -    uint16_t *res_len) > +zxdh_vf_mtr_hw_profile_cfg(struct zxdh_hw *pf_hw, uint16_t vport, > +        uint16_t pcieid __rte_unused, void *cfg_data, > +        void *res_info, uint16_t *res_len) >  { >      int ret = 0; >   > @@ -2203,7 +2216,8 @@ zxdh_vf_mtr_hw_profile_cfg(struct zxdh_hw *pf_hw __rte_unused, >  } >   >  static int > -zxdh_vf_vlan_tpid_set(struct zxdh_hw *pf_hw, uint16_t vport, void *cfg_data, > +zxdh_vf_vlan_tpid_set(struct zxdh_hw *pf_hw, uint16_t vport, > +        uint16_t pcieid __rte_unused, void *cfg_data, >          void *res_info, uint16_t *res_len) >  { >      struct zxdh_vlan_tpid *vlan_tpid = (struct zxdh_vlan_tpid *)cfg_data; > @@ -2231,6 +2245,120 @@ zxdh_vf_vlan_tpid_set(struct zxdh_hw *pf_hw, uint16_t vport, void *cfg_data, >      return ret; >  } >   > +static int > +zxdh_vf_flow_hw_add(struct zxdh_hw *pf_hw, uint16_t vport, > +         uint16_t pcieid, void *cfg_data, > +         void *res_info, uint16_t *res_len) > +{ > +    if (!cfg_data || !res_len || !res_info) { > +        PMD_DRV_LOG(ERR, "invalid inparams"); > +        return -1; > +    } > +    struct rte_flow_error error = {0}; > +    int ret = 0; > +    struct zxdh_flow_op_msg  *flow_entry = (struct zxdh_flow_op_msg *)cfg_data; > +    struct zxdh_flow  *dh_flow; > +    ZXDH_SET(msg_reply_body, res_info, flag, ZXDH_REPS_FAIL); > +    *res_len = sizeof(struct zxdh_flow_op_rsp) - 4; > + > +    ret = pf_fd_hw_apply(pf_hw->eth_dev, &flow_entry->dh_flow, &error, vport, pcieid); > +    if (ret) { > +        PMD_DRV_LOG(ERR, "pf 0x%x for vf 0x%x flow add failed ret :%d", > +            pf_hw->vport.vport, vport, ret); > +        return -1; > +    } > +    void *flow_rsp_addr = ZXDH_ADDR_OF(msg_reply_body, res_info, flow_rsp); > +    dh_flow = flow_rsp_addr; > +    dh_flow->flowentry.hw_idx = flow_entry->dh_flow.flowentry.hw_idx; > +    ZXDH_SET(msg_reply_body, res_info, flag, ZXDH_REPS_SUCC); > +    return 0; > +} > + > +static int > +zxdh_vf_flow_hw_del(struct zxdh_hw *pf_hw, uint16_t vport, > +            uint16_t pcieid, void *cfg_data, > +            void *res_info, uint16_t *res_len) > +{ > +    if (!cfg_data || !res_len || !res_info) { > +        PMD_DRV_LOG(ERR, "invalid inparams"); > +        return -1; > +    } > +    struct rte_flow_error error = {0}; > +    int ret = 0; > +    struct zxdh_flow_op_msg  *flow_entry = (struct zxdh_flow_op_msg *)cfg_data; > +    ZXDH_SET(msg_reply_body, res_info, flag, ZXDH_REPS_FAIL); > +    *res_len = sizeof(struct zxdh_flow_op_rsp) - 4; > + > +    ret = pf_fd_hw_destroy(pf_hw->eth_dev, &flow_entry->dh_flow, &error, vport, pcieid); > +    if (ret) { > +        PMD_DRV_LOG(ERR, "pf 0x%x for vf 0x%x flow del failed ret :%d", > +            pf_hw->vport.vport, vport, ret); > +        return -1; > +    } > +    ZXDH_SET(msg_reply_body, res_info, flag, ZXDH_REPS_SUCC); > +    return 0; > +} > + > +static int > +zxdh_vf_flow_hw_get(struct zxdh_hw *pf_hw, uint16_t vport, > +        uint16_t pcieid __rte_unused, void *cfg_data, > +        void *res_info, uint16_t *res_len) > +{ > +    if (!cfg_data || !res_len || !res_info) { > +        PMD_DRV_LOG(ERR, "invalid inparams"); > +        return -1; > +    } > + > +    void *flow_rsp_addr = ZXDH_ADDR_OF(msg_reply_body, res_info, flow_rsp); > +    void *count_addr = (uint8_t *)flow_rsp_addr + sizeof(struct zxdh_flow); > +    struct rte_flow_error error = {0}; > +    int ret = 0; > +    struct zxdh_flow_op_msg  *flow_entry = (struct zxdh_flow_op_msg *)cfg_data; > +    struct zxdh_flow  *dh_flow; > + > +    ZXDH_SET(msg_reply_body, res_info, flag, ZXDH_REPS_FAIL); > +    *res_len = sizeof(struct zxdh_flow_op_rsp) - 4; > + > +    PMD_DRV_LOG(INFO, "handle %d", flow_entry->dh_flow.flowentry.hw_idx); > +    ret = pf_fd_hw_query_count(pf_hw->eth_dev, &flow_entry->dh_flow, count_addr, &error); > +    if (ret) { > +        PMD_DRV_LOG(DEBUG, "pf 0x%x for vf 0x%x flow get failed ret :%d", > +            pf_hw->vport.vport, vport, ret); > +        return -1; > +    } > +    PMD_DRV_LOG(INFO, " res len :%d", *res_len); > +    dh_flow = flow_rsp_addr; > +    rte_memcpy(&dh_flow->flowentry, &flow_entry->dh_flow.flowentry, sizeof(dh_flow->flowentry)); > +    ZXDH_SET(msg_reply_body, res_info, flag, ZXDH_REPS_SUCC); > +    return 0; > +} > + > +static int > +zxdh_vf_flow_hw_flush(struct zxdh_hw *pf_hw, uint16_t vport, > +            uint16_t pcieid __rte_unused, void *cfg_data, > +            void *res_info, uint16_t *res_len) > +{ > +    if (!cfg_data || !res_len || !res_info) { > +        PMD_DRV_LOG(ERR, "invalid inparams"); > +        return -1; > +    } > +    int ret = 0; > +    uint16_t queue_id = pf_hw->dev_sd->dtb_sd.queueid; > + > +    ZXDH_SET(msg_reply_body, res_info, flag, ZXDH_REPS_FAIL); > +    *res_len = sizeof(struct zxdh_flow_op_rsp) - 4; > + > +    ret = zxdh_np_dtb_acl_offline_delete(pf_hw->dev_id, queue_id, ZXDH_SDT_FD_TABLE, > +                vport, ZXDH_FLOW_STATS_INGRESS_BASE, 1); > +    if (ret) { > +        PMD_DRV_LOG(ERR, "flow flush failed. code:%d", ret); > +        return -1; > +    } > + > +    ZXDH_SET(msg_reply_body, res_info, flag, ZXDH_REPS_SUCC); > +    return 0; > +} > + >  static const zxdh_msg_process_callback zxdh_proc_cb[] = { >      [ZXDH_NULL] = NULL, >      [ZXDH_VF_PORT_INIT] = zxdh_vf_port_init, > @@ -2255,6 +2383,10 @@ static const zxdh_msg_process_callback zxdh_proc_cb[] = { >      [ZXDH_PLCR_CAR_PROFILE_ID_DELETE] =  zxdh_vf_mtr_hw_profile_del, >      [ZXDH_PLCR_CAR_QUEUE_CFG_SET] = zxdh_vf_mtr_hw_plcrflow_cfg, >      [ZXDH_PLCR_CAR_PROFILE_CFG_SET] = zxdh_vf_mtr_hw_profile_cfg, > +    [ZXDH_FLOW_HW_ADD] = zxdh_vf_flow_hw_add, > +    [ZXDH_FLOW_HW_DEL] = zxdh_vf_flow_hw_del, > +    [ZXDH_FLOW_HW_GET] = zxdh_vf_flow_hw_get, > +    [ZXDH_FLOW_HW_FLUSH] = zxdh_vf_flow_hw_flush, >  }; >   >  static inline int > @@ -2269,7 +2401,7 @@ zxdh_config_process_callback(struct zxdh_hw *hw, struct zxdh_msg_info *msg_info, >          return -1; >      } >      if (zxdh_proc_cb[msghead->msg_type]) { > -        ret = zxdh_proc_cb[msghead->msg_type](hw, msghead->vport, > +        ret = zxdh_proc_cb[msghead->msg_type](hw, msghead->vport, msghead->pcieid, >                      (void *)&msg_info->data, res, res_len); >          if (!ret) >              ZXDH_SET(msg_reply_body, res, flag, ZXDH_REPS_SUCC); > diff --git a/drivers/net/zxdh/zxdh_msg.h b/drivers/net/zxdh/zxdh_msg.h > index 579d938a0d..61a3da878e 100644 > --- a/drivers/net/zxdh/zxdh_msg.h > +++ b/drivers/net/zxdh/zxdh_msg.h > @@ -241,6 +241,11 @@ enum zxdh_msg_type { >      ZXDH_PLCR_CAR_QUEUE_CFG_SET = 40, >      ZXDH_PORT_METER_STAT_GET = 42, >   > +    ZXDH_FLOW_HW_ADD = 46, > +    ZXDH_FLOW_HW_DEL = 47, > +    ZXDH_FLOW_HW_GET = 48, > +    ZXDH_FLOW_HW_FLUSH = 49, > + >      ZXDH_MSG_TYPE_END, >  }; >   > @@ -419,6 +424,21 @@ struct zxdh_ifc_mtr_profile_info_bits { >      uint8_t profile_id[0x40]; >  }; >   > +struct err_reason { > +    uint8_t err_type; > +    uint8_t rsv[3]; > +    char reason[512]; > +}; > + > +struct zxdh_flow_op_rsp { > +    struct zxdh_flow  dh_flow; > +    uint8_t rev[4]; > +    union { > +        struct rte_flow_query_count count; > +        struct err_reason error; > +    }; > +}; > + >  struct zxdh_ifc_msg_reply_body_bits { >      uint8_t flag[0x8]; >      union { > @@ -433,6 +453,7 @@ struct zxdh_ifc_msg_reply_body_bits { >          struct zxdh_ifc_agent_mac_module_eeprom_msg_bits module_eeprom_msg; >          struct zxdh_ifc_mtr_profile_info_bits  mtr_profile_info; >          struct zxdh_ifc_mtr_stats_bits hw_mtr_stats; > +        struct zxdh_flow_op_rsp  flow_rsp; >      }; >  }; >   > @@ -540,6 +561,10 @@ struct zxdh_vlan_tpid { >      uint16_t tpid; >  }; >   > +struct zxdh_flow_op_msg { > +    struct zxdh_flow dh_flow; > +}; > + >  struct zxdh_msg_info { >      union { >          uint8_t head_len[ZXDH_MSG_HEAD_LEN]; > @@ -567,6 +592,7 @@ struct zxdh_msg_info { >          struct zxdh_plcr_profile_cfg zxdh_plcr_profile_cfg; >          struct zxdh_plcr_flow_cfg  zxdh_plcr_flow_cfg; >          struct zxdh_mtr_stats_query  zxdh_mtr_stats_query; > +        struct zxdh_flow_op_msg flow_msg; >      } data; >  }; >   > @@ -594,8 +620,9 @@ struct zxdh_inic_recv_msg { >   >  typedef int (*zxdh_bar_chan_msg_recv_callback)(void *pay_load, uint16_t len, >          void *reps_buffer, uint16_t *reps_len, void *dev); > -typedef int (*zxdh_msg_process_callback)(struct zxdh_hw *hw, uint16_t vport, void *cfg_data, > -    void *res_info, uint16_t *res_len); > +typedef int (*zxdh_msg_process_callback)(struct zxdh_hw *hw, uint16_t vport, > +        uint16_t pcieid, void *cfg_data, > +        void *res_info, uint16_t *res_len); >   >  typedef int (*zxdh_bar_chan_msg_recv_callback)(void *pay_load, uint16_t len, >              void *reps_buffer, uint16_t *reps_len, void *dev); > diff --git a/drivers/net/zxdh/zxdh_tables.h b/drivers/net/zxdh/zxdh_tables.h > index 3280ff1f89..8cfc833333 100644 > --- a/drivers/net/zxdh/zxdh_tables.h > +++ b/drivers/net/zxdh/zxdh_tables.h > @@ -7,6 +7,8 @@ >   >  #include  >   > +#include  > + >  /* eram */ >  #define ZXDH_SDT_VPORT_ATT_TABLE          1 >  #define ZXDH_SDT_PANEL_ATT_TABLE          2 > @@ -16,6 +18,8 @@ >  #define ZXDH_SDT_UNICAST_ATT_TABLE        10 >  #define ZXDH_SDT_MULTICAST_ATT_TABLE      11 >  #define ZXDH_SDT_PORT_VLAN_ATT_TABLE      16 > +#define ZXDH_SDT_TUNNEL_ENCAP0_TABLE      28 > +#define ZXDH_SDT_TUNNEL_ENCAP1_TABLE      29 >  /* hash */ >  #define ZXDH_SDT_L2_ENTRY_TABLE0          64 >  #define ZXDH_SDT_L2_ENTRY_TABLE1          65 > @@ -27,12 +31,14 @@ >  #define ZXDH_SDT_MC_TABLE2                78 >  #define ZXDH_SDT_MC_TABLE3                79 >   > +#define ZXDH_SDT_FD_TABLE                 130 > + >  #define ZXDH_PORT_VHCA_FLAG                       1 >  #define ZXDH_PORT_RSS_HASH_FACTOR_FLAG            3 >  #define ZXDH_PORT_HASH_ALG_FLAG                   4 >  #define ZXDH_PORT_PHY_PORT_FLAG                   5 >  #define ZXDH_PORT_LAG_ID_FLAG                     6 > - > +#define ZXDH_PORT_VXLAN_OFFLOAD_EN_OFF            7 >  #define ZXDH_PORT_PF_VQM_VFID_FLAG                8 >   >  #define ZXDH_PORT_MTU_FLAG                        10 > @@ -169,7 +175,7 @@ struct zxdh_port_attr_table { >      uint8_t phy_port: 4; >   >      uint16_t lag_id : 3; > -    uint16_t rsv81 : 1; > +    uint16_t fd_vxlan_offload_en : 1; >      uint16_t pf_vfid : 11; >      uint16_t rsv82 : 1; >   > --  > 2.27.0 > >