net/hns3: support flow director
[dpdk.git] / drivers / net / hns3 / hns3_ethdev.c
index f0a2e36..b8e72cb 100644 (file)
@@ -28,6 +28,8 @@
 #define HNS3_DEFAULT_PORT_CONF_BURST_SIZE      32
 #define HNS3_DEFAULT_PORT_CONF_QUEUES_NUM      1
 
+#define HNS3_SERVICE_INTERVAL          1000000 /* us */
+
 int hns3_logtype_init;
 int hns3_logtype_driver;
 
@@ -143,6 +145,772 @@ hns3_uninit_umv_space(struct hns3_hw *hw)
        return 0;
 }
 
+static bool
+hns3_is_umv_space_full(struct hns3_hw *hw)
+{
+       struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw);
+       struct hns3_pf *pf = &hns->pf;
+       bool is_full;
+
+       is_full = (pf->used_umv_size >= pf->max_umv_size);
+
+       return is_full;
+}
+
+static void
+hns3_update_umv_space(struct hns3_hw *hw, bool is_free)
+{
+       struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw);
+       struct hns3_pf *pf = &hns->pf;
+
+       if (is_free) {
+               if (pf->used_umv_size > 0)
+                       pf->used_umv_size--;
+       } else
+               pf->used_umv_size++;
+}
+
+static void
+hns3_prepare_mac_addr(struct hns3_mac_vlan_tbl_entry_cmd *new_req,
+                     const uint8_t *addr, bool is_mc)
+{
+       const unsigned char *mac_addr = addr;
+       uint32_t high_val = ((uint32_t)mac_addr[3] << 24) |
+                           ((uint32_t)mac_addr[2] << 16) |
+                           ((uint32_t)mac_addr[1] << 8) |
+                           (uint32_t)mac_addr[0];
+       uint32_t low_val = ((uint32_t)mac_addr[5] << 8) | (uint32_t)mac_addr[4];
+
+       hns3_set_bit(new_req->flags, HNS3_MAC_VLAN_BIT0_EN_B, 1);
+       if (is_mc) {
+               hns3_set_bit(new_req->entry_type, HNS3_MAC_VLAN_BIT0_EN_B, 0);
+               hns3_set_bit(new_req->entry_type, HNS3_MAC_VLAN_BIT1_EN_B, 1);
+               hns3_set_bit(new_req->mc_mac_en, HNS3_MAC_VLAN_BIT0_EN_B, 1);
+       }
+
+       new_req->mac_addr_hi32 = rte_cpu_to_le_32(high_val);
+       new_req->mac_addr_lo16 = rte_cpu_to_le_16(low_val & 0xffff);
+}
+
+static int
+hns3_get_mac_vlan_cmd_status(struct hns3_hw *hw, uint16_t cmdq_resp,
+                            uint8_t resp_code,
+                            enum hns3_mac_vlan_tbl_opcode op)
+{
+       if (cmdq_resp) {
+               hns3_err(hw, "cmdq execute failed for get_mac_vlan_cmd_status,status=%u",
+                        cmdq_resp);
+               return -EIO;
+       }
+
+       if (op == HNS3_MAC_VLAN_ADD) {
+               if (resp_code == 0 || resp_code == 1) {
+                       return 0;
+               } else if (resp_code == HNS3_ADD_UC_OVERFLOW) {
+                       hns3_err(hw, "add mac addr failed for uc_overflow");
+                       return -ENOSPC;
+               } else if (resp_code == HNS3_ADD_MC_OVERFLOW) {
+                       hns3_err(hw, "add mac addr failed for mc_overflow");
+                       return -ENOSPC;
+               }
+
+               hns3_err(hw, "add mac addr failed for undefined, code=%u",
+                        resp_code);
+               return -EIO;
+       } else if (op == HNS3_MAC_VLAN_REMOVE) {
+               if (resp_code == 0) {
+                       return 0;
+               } else if (resp_code == 1) {
+                       hns3_dbg(hw, "remove mac addr failed for miss");
+                       return -ENOENT;
+               }
+
+               hns3_err(hw, "remove mac addr failed for undefined, code=%u",
+                        resp_code);
+               return -EIO;
+       } else if (op == HNS3_MAC_VLAN_LKUP) {
+               if (resp_code == 0) {
+                       return 0;
+               } else if (resp_code == 1) {
+                       hns3_dbg(hw, "lookup mac addr failed for miss");
+                       return -ENOENT;
+               }
+
+               hns3_err(hw, "lookup mac addr failed for undefined, code=%u",
+                        resp_code);
+               return -EIO;
+       }
+
+       hns3_err(hw, "unknown opcode for get_mac_vlan_cmd_status, opcode=%u",
+                op);
+
+       return -EINVAL;
+}
+
+static int
+hns3_lookup_mac_vlan_tbl(struct hns3_hw *hw,
+                        struct hns3_mac_vlan_tbl_entry_cmd *req,
+                        struct hns3_cmd_desc *desc, bool is_mc)
+{
+       uint8_t resp_code;
+       uint16_t retval;
+       int ret;
+
+       hns3_cmd_setup_basic_desc(&desc[0], HNS3_OPC_MAC_VLAN_ADD, true);
+       if (is_mc) {
+               desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT);
+               memcpy(desc[0].data, req,
+                          sizeof(struct hns3_mac_vlan_tbl_entry_cmd));
+               hns3_cmd_setup_basic_desc(&desc[1], HNS3_OPC_MAC_VLAN_ADD,
+                                         true);
+               desc[1].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT);
+               hns3_cmd_setup_basic_desc(&desc[2], HNS3_OPC_MAC_VLAN_ADD,
+                                         true);
+               ret = hns3_cmd_send(hw, desc, HNS3_MC_MAC_VLAN_ADD_DESC_NUM);
+       } else {
+               memcpy(desc[0].data, req,
+                      sizeof(struct hns3_mac_vlan_tbl_entry_cmd));
+               ret = hns3_cmd_send(hw, desc, 1);
+       }
+       if (ret) {
+               hns3_err(hw, "lookup mac addr failed for cmd_send, ret =%d.",
+                        ret);
+               return ret;
+       }
+       resp_code = (rte_le_to_cpu_32(desc[0].data[0]) >> 8) & 0xff;
+       retval = rte_le_to_cpu_16(desc[0].retval);
+
+       return hns3_get_mac_vlan_cmd_status(hw, retval, resp_code,
+                                           HNS3_MAC_VLAN_LKUP);
+}
+
+static int
+hns3_add_mac_vlan_tbl(struct hns3_hw *hw,
+                     struct hns3_mac_vlan_tbl_entry_cmd *req,
+                     struct hns3_cmd_desc *mc_desc)
+{
+       uint8_t resp_code;
+       uint16_t retval;
+       int cfg_status;
+       int ret;
+
+       if (mc_desc == NULL) {
+               struct hns3_cmd_desc desc;
+
+               hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_MAC_VLAN_ADD, false);
+               memcpy(desc.data, req,
+                      sizeof(struct hns3_mac_vlan_tbl_entry_cmd));
+               ret = hns3_cmd_send(hw, &desc, 1);
+               resp_code = (rte_le_to_cpu_32(desc.data[0]) >> 8) & 0xff;
+               retval = rte_le_to_cpu_16(desc.retval);
+
+               cfg_status = hns3_get_mac_vlan_cmd_status(hw, retval, resp_code,
+                                                         HNS3_MAC_VLAN_ADD);
+       } else {
+               hns3_cmd_reuse_desc(&mc_desc[0], false);
+               mc_desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT);
+               hns3_cmd_reuse_desc(&mc_desc[1], false);
+               mc_desc[1].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT);
+               hns3_cmd_reuse_desc(&mc_desc[2], false);
+               mc_desc[2].flag &= rte_cpu_to_le_16(~HNS3_CMD_FLAG_NEXT);
+               memcpy(mc_desc[0].data, req,
+                      sizeof(struct hns3_mac_vlan_tbl_entry_cmd));
+               mc_desc[0].retval = 0;
+               ret = hns3_cmd_send(hw, mc_desc, HNS3_MC_MAC_VLAN_ADD_DESC_NUM);
+               resp_code = (rte_le_to_cpu_32(mc_desc[0].data[0]) >> 8) & 0xff;
+               retval = rte_le_to_cpu_16(mc_desc[0].retval);
+
+               cfg_status = hns3_get_mac_vlan_cmd_status(hw, retval, resp_code,
+                                                         HNS3_MAC_VLAN_ADD);
+       }
+
+       if (ret) {
+               hns3_err(hw, "add mac addr failed for cmd_send, ret =%d", ret);
+               return ret;
+       }
+
+       return cfg_status;
+}
+
+static int
+hns3_remove_mac_vlan_tbl(struct hns3_hw *hw,
+                        struct hns3_mac_vlan_tbl_entry_cmd *req)
+{
+       struct hns3_cmd_desc desc;
+       uint8_t resp_code;
+       uint16_t retval;
+       int ret;
+
+       hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_MAC_VLAN_REMOVE, false);
+
+       memcpy(desc.data, req, sizeof(struct hns3_mac_vlan_tbl_entry_cmd));
+
+       ret = hns3_cmd_send(hw, &desc, 1);
+       if (ret) {
+               hns3_err(hw, "del mac addr failed for cmd_send, ret =%d", ret);
+               return ret;
+       }
+       resp_code = (rte_le_to_cpu_32(desc.data[0]) >> 8) & 0xff;
+       retval = rte_le_to_cpu_16(desc.retval);
+
+       return hns3_get_mac_vlan_cmd_status(hw, retval, resp_code,
+                                           HNS3_MAC_VLAN_REMOVE);
+}
+
+static int
+hns3_add_uc_addr_common(struct hns3_hw *hw, struct rte_ether_addr *mac_addr)
+{
+       struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw);
+       struct hns3_mac_vlan_tbl_entry_cmd req;
+       struct hns3_pf *pf = &hns->pf;
+       struct hns3_cmd_desc desc;
+       char mac_str[RTE_ETHER_ADDR_FMT_SIZE];
+       uint16_t egress_port = 0;
+       uint8_t vf_id;
+       int ret;
+
+       /* check if mac addr is valid */
+       if (!rte_is_valid_assigned_ether_addr(mac_addr)) {
+               rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE,
+                                     mac_addr);
+               hns3_err(hw, "Add unicast mac addr err! addr(%s) invalid",
+                        mac_str);
+               return -EINVAL;
+       }
+
+       memset(&req, 0, sizeof(req));
+
+       /*
+        * In current version VF is not supported when PF is driven by DPDK
+        * driver, the PF-related vf_id is 0, just need to configure parameters
+        * for vf_id 0.
+        */
+       vf_id = 0;
+       hns3_set_field(egress_port, HNS3_MAC_EPORT_VFID_M,
+                      HNS3_MAC_EPORT_VFID_S, vf_id);
+
+       req.egress_port = rte_cpu_to_le_16(egress_port);
+
+       hns3_prepare_mac_addr(&req, mac_addr->addr_bytes, false);
+
+       /*
+        * Lookup the mac address in the mac_vlan table, and add
+        * it if the entry is inexistent. Repeated unicast entry
+        * is not allowed in the mac vlan table.
+        */
+       ret = hns3_lookup_mac_vlan_tbl(hw, &req, &desc, false);
+       if (ret == -ENOENT) {
+               if (!hns3_is_umv_space_full(hw)) {
+                       ret = hns3_add_mac_vlan_tbl(hw, &req, NULL);
+                       if (!ret)
+                               hns3_update_umv_space(hw, false);
+                       return ret;
+               }
+
+               hns3_err(hw, "UC MAC table full(%u)", pf->used_umv_size);
+
+               return -ENOSPC;
+       }
+
+       rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, mac_addr);
+
+       /* check if we just hit the duplicate */
+       if (ret == 0) {
+               hns3_dbg(hw, "mac addr(%s) has been in the MAC table", mac_str);
+               return 0;
+       }
+
+       hns3_err(hw, "PF failed to add unicast entry(%s) in the MAC table",
+                mac_str);
+
+       return ret;
+}
+
+static int
+hns3_add_mac_addr(struct rte_eth_dev *dev, struct rte_ether_addr *mac_addr,
+                 uint32_t idx, __attribute__ ((unused)) uint32_t pool)
+{
+       struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+       char mac_str[RTE_ETHER_ADDR_FMT_SIZE];
+       int ret;
+
+       rte_spinlock_lock(&hw->lock);
+       ret = hns3_add_uc_addr_common(hw, mac_addr);
+       if (ret) {
+               rte_spinlock_unlock(&hw->lock);
+               rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE,
+                                     mac_addr);
+               hns3_err(hw, "Failed to add mac addr(%s): %d", mac_str, ret);
+               return ret;
+       }
+
+       if (idx == 0)
+               hw->mac.default_addr_setted = true;
+       rte_spinlock_unlock(&hw->lock);
+
+       return ret;
+}
+
+static int
+hns3_remove_uc_addr_common(struct hns3_hw *hw, struct rte_ether_addr *mac_addr)
+{
+       struct hns3_mac_vlan_tbl_entry_cmd req;
+       char mac_str[RTE_ETHER_ADDR_FMT_SIZE];
+       int ret;
+
+       /* check if mac addr is valid */
+       if (!rte_is_valid_assigned_ether_addr(mac_addr)) {
+               rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE,
+                                     mac_addr);
+               hns3_err(hw, "Remove unicast mac addr err! addr(%s) invalid",
+                        mac_str);
+               return -EINVAL;
+       }
+
+       memset(&req, 0, sizeof(req));
+       hns3_set_bit(req.entry_type, HNS3_MAC_VLAN_BIT0_EN_B, 0);
+       hns3_prepare_mac_addr(&req, mac_addr->addr_bytes, false);
+       ret = hns3_remove_mac_vlan_tbl(hw, &req);
+       if (ret == -ENOENT) /* mac addr isn't existent in the mac vlan table. */
+               return 0;
+       else if (ret == 0)
+               hns3_update_umv_space(hw, true);
+
+       return ret;
+}
+
+static void
+hns3_remove_mac_addr(struct rte_eth_dev *dev, uint32_t idx)
+{
+       struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+       /* index will be checked by upper level rte interface */
+       struct rte_ether_addr *mac_addr = &dev->data->mac_addrs[idx];
+       char mac_str[RTE_ETHER_ADDR_FMT_SIZE];
+       int ret;
+
+       rte_spinlock_lock(&hw->lock);
+       ret = hns3_remove_uc_addr_common(hw, mac_addr);
+       if (ret) {
+               rte_spinlock_unlock(&hw->lock);
+               rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE,
+                                     mac_addr);
+               hns3_err(hw, "Failed to remove mac addr(%s): %d", mac_str, ret);
+               return;
+       }
+
+       if (idx == 0)
+               hw->mac.default_addr_setted = false;
+       rte_spinlock_unlock(&hw->lock);
+}
+
+static int
+hns3_set_default_mac_addr(struct rte_eth_dev *dev,
+                         struct rte_ether_addr *mac_addr)
+{
+       struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+       struct rte_ether_addr *oaddr;
+       char mac_str[RTE_ETHER_ADDR_FMT_SIZE];
+       bool default_addr_setted;
+       bool rm_succes = false;
+       int ret, ret_val;
+
+       /* check if mac addr is valid */
+       if (!rte_is_valid_assigned_ether_addr(mac_addr)) {
+               rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE,
+                                     mac_addr);
+               hns3_err(hw, "Failed to set mac addr, addr(%s) invalid",
+                        mac_str);
+               return -EINVAL;
+       }
+
+       oaddr = (struct rte_ether_addr *)hw->mac.mac_addr;
+       default_addr_setted = hw->mac.default_addr_setted;
+       if (default_addr_setted && !!rte_is_same_ether_addr(mac_addr, oaddr))
+               return 0;
+
+       rte_spinlock_lock(&hw->lock);
+       if (default_addr_setted) {
+               ret = hns3_remove_uc_addr_common(hw, oaddr);
+               if (ret) {
+                       rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE,
+                                             oaddr);
+                       hns3_warn(hw, "Remove old uc mac address(%s) fail: %d",
+                                 mac_str, ret);
+                       rm_succes = false;
+               } else
+                       rm_succes = true;
+       }
+
+       ret = hns3_add_uc_addr_common(hw, mac_addr);
+       if (ret) {
+               rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE,
+                                     mac_addr);
+               hns3_err(hw, "Failed to set mac addr(%s): %d", mac_str, ret);
+               goto err_add_uc_addr;
+       }
+
+       rte_ether_addr_copy(mac_addr,
+                           (struct rte_ether_addr *)hw->mac.mac_addr);
+       hw->mac.default_addr_setted = true;
+       rte_spinlock_unlock(&hw->lock);
+
+       return 0;
+
+err_add_uc_addr:
+       if (rm_succes) {
+               ret_val = hns3_add_uc_addr_common(hw, oaddr);
+               if (ret_val) {
+                       rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE,
+                                             oaddr);
+                       hns3_warn(hw,
+                                 "Failed to restore old uc mac addr(%s): %d",
+                                 mac_str, ret_val);
+                       hw->mac.default_addr_setted = false;
+               }
+       }
+       rte_spinlock_unlock(&hw->lock);
+
+       return ret;
+}
+
+static void
+hns3_update_desc_vfid(struct hns3_cmd_desc *desc, uint8_t vfid, bool clr)
+{
+#define HNS3_VF_NUM_IN_FIRST_DESC 192
+       uint8_t word_num;
+       uint8_t bit_num;
+
+       if (vfid < HNS3_VF_NUM_IN_FIRST_DESC) {
+               word_num = vfid / 32;
+               bit_num = vfid % 32;
+               if (clr)
+                       desc[1].data[word_num] &=
+                           rte_cpu_to_le_32(~(1UL << bit_num));
+               else
+                       desc[1].data[word_num] |=
+                           rte_cpu_to_le_32(1UL << bit_num);
+       } else {
+               word_num = (vfid - HNS3_VF_NUM_IN_FIRST_DESC) / 32;
+               bit_num = vfid % 32;
+               if (clr)
+                       desc[2].data[word_num] &=
+                           rte_cpu_to_le_32(~(1UL << bit_num));
+               else
+                       desc[2].data[word_num] |=
+                           rte_cpu_to_le_32(1UL << bit_num);
+       }
+}
+
+static int
+hns3_add_mc_addr(struct hns3_hw *hw, struct rte_ether_addr *mac_addr)
+{
+       struct hns3_mac_vlan_tbl_entry_cmd req;
+       struct hns3_cmd_desc desc[3];
+       char mac_str[RTE_ETHER_ADDR_FMT_SIZE];
+       uint8_t vf_id;
+       int ret;
+
+       /* Check if mac addr is valid */
+       if (!rte_is_multicast_ether_addr(mac_addr)) {
+               rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE,
+                                     mac_addr);
+               hns3_err(hw, "Failed to add mc mac addr, addr(%s) invalid",
+                        mac_str);
+               return -EINVAL;
+       }
+
+       memset(&req, 0, sizeof(req));
+       hns3_set_bit(req.entry_type, HNS3_MAC_VLAN_BIT0_EN_B, 0);
+       hns3_prepare_mac_addr(&req, mac_addr->addr_bytes, true);
+       ret = hns3_lookup_mac_vlan_tbl(hw, &req, desc, true);
+       if (ret) {
+               /* This mac addr do not exist, add new entry for it */
+               memset(desc[0].data, 0, sizeof(desc[0].data));
+               memset(desc[1].data, 0, sizeof(desc[0].data));
+               memset(desc[2].data, 0, sizeof(desc[0].data));
+       }
+
+       /*
+        * In current version VF is not supported when PF is driven by DPDK
+        * driver, the PF-related vf_id is 0, just need to configure parameters
+        * for vf_id 0.
+        */
+       vf_id = 0;
+       hns3_update_desc_vfid(desc, vf_id, false);
+       ret = hns3_add_mac_vlan_tbl(hw, &req, desc);
+       if (ret) {
+               if (ret == -ENOSPC)
+                       hns3_err(hw, "mc mac vlan table is full");
+               rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE,
+                                     mac_addr);
+               hns3_err(hw, "Failed to add mc mac addr(%s): %d", mac_str, ret);
+       }
+
+       return ret;
+}
+
+static int
+hns3_remove_mc_addr(struct hns3_hw *hw, struct rte_ether_addr *mac_addr)
+{
+       struct hns3_mac_vlan_tbl_entry_cmd req;
+       struct hns3_cmd_desc desc[3];
+       char mac_str[RTE_ETHER_ADDR_FMT_SIZE];
+       uint8_t vf_id;
+       int ret;
+
+       /* Check if mac addr is valid */
+       if (!rte_is_multicast_ether_addr(mac_addr)) {
+               rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE,
+                                     mac_addr);
+               hns3_err(hw, "Failed to rm mc mac addr, addr(%s) invalid",
+                        mac_str);
+               return -EINVAL;
+       }
+
+       memset(&req, 0, sizeof(req));
+       hns3_set_bit(req.entry_type, HNS3_MAC_VLAN_BIT0_EN_B, 0);
+       hns3_prepare_mac_addr(&req, mac_addr->addr_bytes, true);
+       ret = hns3_lookup_mac_vlan_tbl(hw, &req, desc, true);
+       if (ret == 0) {
+               /*
+                * This mac addr exist, remove this handle's VFID for it.
+                * In current version VF is not supported when PF is driven by
+                * DPDK driver, the PF-related vf_id is 0, just need to
+                * configure parameters for vf_id 0.
+                */
+               vf_id = 0;
+               hns3_update_desc_vfid(desc, vf_id, true);
+
+               /* All the vfid is zero, so need to delete this entry */
+               ret = hns3_remove_mac_vlan_tbl(hw, &req);
+       } else if (ret == -ENOENT) {
+               /* This mac addr doesn't exist. */
+               return 0;
+       }
+
+       if (ret) {
+               rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE,
+                                     mac_addr);
+               hns3_err(hw, "Failed to rm mc mac addr(%s): %d", mac_str, ret);
+       }
+
+       return ret;
+}
+
+static int
+hns3_set_mc_addr_chk_param(struct hns3_hw *hw,
+                          struct rte_ether_addr *mc_addr_set,
+                          uint32_t nb_mc_addr)
+{
+       char mac_str[RTE_ETHER_ADDR_FMT_SIZE];
+       struct rte_ether_addr *addr;
+       uint32_t i;
+       uint32_t j;
+
+       if (nb_mc_addr > HNS3_MC_MACADDR_NUM) {
+               hns3_err(hw, "Failed to set mc mac addr, nb_mc_addr(%d) "
+                        "invalid. valid range: 0~%d",
+                        nb_mc_addr, HNS3_MC_MACADDR_NUM);
+               return -EINVAL;
+       }
+
+       /* Check if input mac addresses are valid */
+       for (i = 0; i < nb_mc_addr; i++) {
+               addr = &mc_addr_set[i];
+               if (!rte_is_multicast_ether_addr(addr)) {
+                       rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE,
+                                             addr);
+                       hns3_err(hw,
+                                "Failed to set mc mac addr, addr(%s) invalid.",
+                                mac_str);
+                       return -EINVAL;
+               }
+
+               /* Check if there are duplicate addresses */
+               for (j = i + 1; j < nb_mc_addr; j++) {
+                       if (rte_is_same_ether_addr(addr, &mc_addr_set[j])) {
+                               rte_ether_format_addr(mac_str,
+                                                     RTE_ETHER_ADDR_FMT_SIZE,
+                                                     addr);
+                               hns3_err(hw, "Failed to set mc mac addr, "
+                                        "addrs invalid. two same addrs(%s).",
+                                        mac_str);
+                               return -EINVAL;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static void
+hns3_set_mc_addr_calc_addr(struct hns3_hw *hw,
+                          struct rte_ether_addr *mc_addr_set,
+                          int mc_addr_num,
+                          struct rte_ether_addr *reserved_addr_list,
+                          int *reserved_addr_num,
+                          struct rte_ether_addr *add_addr_list,
+                          int *add_addr_num,
+                          struct rte_ether_addr *rm_addr_list,
+                          int *rm_addr_num)
+{
+       struct rte_ether_addr *addr;
+       int current_addr_num;
+       int reserved_num = 0;
+       int add_num = 0;
+       int rm_num = 0;
+       int num;
+       int i;
+       int j;
+       bool same_addr;
+
+       /* Calculate the mc mac address list that should be removed */
+       current_addr_num = hw->mc_addrs_num;
+       for (i = 0; i < current_addr_num; i++) {
+               addr = &hw->mc_addrs[i];
+               same_addr = false;
+               for (j = 0; j < mc_addr_num; j++) {
+                       if (rte_is_same_ether_addr(addr, &mc_addr_set[j])) {
+                               same_addr = true;
+                               break;
+                       }
+               }
+
+               if (!same_addr) {
+                       rte_ether_addr_copy(addr, &rm_addr_list[rm_num]);
+                       rm_num++;
+               } else {
+                       rte_ether_addr_copy(addr,
+                                           &reserved_addr_list[reserved_num]);
+                       reserved_num++;
+               }
+       }
+
+       /* Calculate the mc mac address list that should be added */
+       for (i = 0; i < mc_addr_num; i++) {
+               addr = &mc_addr_set[i];
+               same_addr = false;
+               for (j = 0; j < current_addr_num; j++) {
+                       if (rte_is_same_ether_addr(addr, &hw->mc_addrs[j])) {
+                               same_addr = true;
+                               break;
+                       }
+               }
+
+               if (!same_addr) {
+                       rte_ether_addr_copy(addr, &add_addr_list[add_num]);
+                       add_num++;
+               }
+       }
+
+       /* Reorder the mc mac address list maintained by driver */
+       for (i = 0; i < reserved_num; i++)
+               rte_ether_addr_copy(&reserved_addr_list[i], &hw->mc_addrs[i]);
+
+       for (i = 0; i < rm_num; i++) {
+               num = reserved_num + i;
+               rte_ether_addr_copy(&rm_addr_list[i], &hw->mc_addrs[num]);
+       }
+
+       *reserved_addr_num = reserved_num;
+       *add_addr_num = add_num;
+       *rm_addr_num = rm_num;
+}
+
+static int
+hns3_set_mc_mac_addr_list(struct rte_eth_dev *dev,
+                         struct rte_ether_addr *mc_addr_set,
+                         uint32_t nb_mc_addr)
+{
+       struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+       struct rte_ether_addr reserved_addr_list[HNS3_MC_MACADDR_NUM];
+       struct rte_ether_addr add_addr_list[HNS3_MC_MACADDR_NUM];
+       struct rte_ether_addr rm_addr_list[HNS3_MC_MACADDR_NUM];
+       struct rte_ether_addr *addr;
+       int reserved_addr_num;
+       int add_addr_num;
+       int rm_addr_num;
+       int mc_addr_num;
+       int num;
+       int ret;
+       int i;
+
+       /* Check if input parameters are valid */
+       ret = hns3_set_mc_addr_chk_param(hw, mc_addr_set, nb_mc_addr);
+       if (ret)
+               return ret;
+
+       rte_spinlock_lock(&hw->lock);
+
+       /*
+        * Calculate the mc mac address lists those should be removed and be
+        * added, Reorder the mc mac address list maintained by driver.
+        */
+       mc_addr_num = (int)nb_mc_addr;
+       hns3_set_mc_addr_calc_addr(hw, mc_addr_set, mc_addr_num,
+                                  reserved_addr_list, &reserved_addr_num,
+                                  add_addr_list, &add_addr_num,
+                                  rm_addr_list, &rm_addr_num);
+
+       /* Remove mc mac addresses */
+       for (i = 0; i < rm_addr_num; i++) {
+               num = rm_addr_num - i - 1;
+               addr = &rm_addr_list[num];
+               ret = hns3_remove_mc_addr(hw, addr);
+               if (ret) {
+                       rte_spinlock_unlock(&hw->lock);
+                       return ret;
+               }
+               hw->mc_addrs_num--;
+       }
+
+       /* Add mc mac addresses */
+       for (i = 0; i < add_addr_num; i++) {
+               addr = &add_addr_list[i];
+               ret = hns3_add_mc_addr(hw, addr);
+               if (ret) {
+                       rte_spinlock_unlock(&hw->lock);
+                       return ret;
+               }
+
+               num = reserved_addr_num + i;
+               rte_ether_addr_copy(addr, &hw->mc_addrs[num]);
+               hw->mc_addrs_num++;
+       }
+       rte_spinlock_unlock(&hw->lock);
+
+       return 0;
+}
+
+static int
+hns3_configure_all_mc_mac_addr(struct hns3_adapter *hns, bool del)
+{
+       char mac_str[RTE_ETHER_ADDR_FMT_SIZE];
+       struct hns3_hw *hw = &hns->hw;
+       struct rte_ether_addr *addr;
+       int err = 0;
+       int ret;
+       int i;
+
+       for (i = 0; i < hw->mc_addrs_num; i++) {
+               addr = &hw->mc_addrs[i];
+               if (!rte_is_multicast_ether_addr(addr))
+                       continue;
+               if (del)
+                       ret = hns3_remove_mc_addr(hw, addr);
+               else
+                       ret = hns3_add_mc_addr(hw, addr);
+               if (ret) {
+                       err = ret;
+                       rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE,
+                                             addr);
+                       hns3_dbg(hw, "%s mc mac addr: %s failed",
+                                del ? "Remove" : "Restore", mac_str);
+               }
+       }
+       return err;
+}
+
 static int
 hns3_set_mac_mtu(struct hns3_hw *hw, uint16_t new_mps)
 {
@@ -178,6 +946,145 @@ hns3_config_mtu(struct hns3_hw *hw, uint16_t mps)
        return 0;
 }
 
+static int
+hns3_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
+{
+       struct hns3_adapter *hns = dev->data->dev_private;
+       uint32_t frame_size = mtu + HNS3_ETH_OVERHEAD;
+       struct hns3_hw *hw = &hns->hw;
+       bool is_jumbo_frame;
+       int ret;
+
+       if (dev->data->dev_started) {
+               hns3_err(hw, "Failed to set mtu, port %u must be stopped "
+                        "before configuration", dev->data->port_id);
+               return -EBUSY;
+       }
+
+       rte_spinlock_lock(&hw->lock);
+       is_jumbo_frame = frame_size > RTE_ETHER_MAX_LEN ? true : false;
+       frame_size = RTE_MAX(frame_size, HNS3_DEFAULT_FRAME_LEN);
+
+       /*
+        * Maximum value of frame_size is HNS3_MAX_FRAME_LEN, so it can safely
+        * assign to "uint16_t" type variable.
+        */
+       ret = hns3_config_mtu(hw, (uint16_t)frame_size);
+       if (ret) {
+               rte_spinlock_unlock(&hw->lock);
+               hns3_err(hw, "Failed to set mtu, port %u mtu %u: %d",
+                        dev->data->port_id, mtu, ret);
+               return ret;
+       }
+       hns->pf.mps = (uint16_t)frame_size;
+       if (is_jumbo_frame)
+               dev->data->dev_conf.rxmode.offloads |=
+                                               DEV_RX_OFFLOAD_JUMBO_FRAME;
+       else
+               dev->data->dev_conf.rxmode.offloads &=
+                                               ~DEV_RX_OFFLOAD_JUMBO_FRAME;
+       dev->data->dev_conf.rxmode.max_rx_pkt_len = frame_size;
+       rte_spinlock_unlock(&hw->lock);
+
+       return 0;
+}
+
+static int
+hns3_dev_infos_get(struct rte_eth_dev *eth_dev, struct rte_eth_dev_info *info)
+{
+       struct hns3_adapter *hns = eth_dev->data->dev_private;
+       struct hns3_hw *hw = &hns->hw;
+
+       info->max_rx_queues = hw->tqps_num;
+       info->max_tx_queues = hw->tqps_num;
+       info->max_rx_pktlen = HNS3_MAX_FRAME_LEN; /* CRC included */
+       info->min_rx_bufsize = hw->rx_buf_len;
+       info->max_mac_addrs = HNS3_UC_MACADDR_NUM;
+       info->max_mtu = info->max_rx_pktlen - HNS3_ETH_OVERHEAD;
+       info->rx_offload_capa = (DEV_RX_OFFLOAD_IPV4_CKSUM |
+                                DEV_RX_OFFLOAD_TCP_CKSUM |
+                                DEV_RX_OFFLOAD_UDP_CKSUM |
+                                DEV_RX_OFFLOAD_SCTP_CKSUM |
+                                DEV_RX_OFFLOAD_OUTER_IPV4_CKSUM |
+                                DEV_RX_OFFLOAD_OUTER_UDP_CKSUM |
+                                DEV_RX_OFFLOAD_KEEP_CRC |
+                                DEV_RX_OFFLOAD_SCATTER |
+                                DEV_RX_OFFLOAD_VLAN_STRIP |
+                                DEV_RX_OFFLOAD_QINQ_STRIP |
+                                DEV_RX_OFFLOAD_VLAN_FILTER |
+                                DEV_RX_OFFLOAD_VLAN_EXTEND |
+                                DEV_RX_OFFLOAD_JUMBO_FRAME);
+       info->tx_queue_offload_capa = DEV_TX_OFFLOAD_MBUF_FAST_FREE;
+       info->tx_offload_capa = (DEV_TX_OFFLOAD_OUTER_IPV4_CKSUM |
+                                DEV_TX_OFFLOAD_IPV4_CKSUM |
+                                DEV_TX_OFFLOAD_TCP_CKSUM |
+                                DEV_TX_OFFLOAD_UDP_CKSUM |
+                                DEV_TX_OFFLOAD_SCTP_CKSUM |
+                                DEV_TX_OFFLOAD_VLAN_INSERT |
+                                DEV_TX_OFFLOAD_QINQ_INSERT |
+                                DEV_TX_OFFLOAD_MULTI_SEGS |
+                                info->tx_queue_offload_capa);
+
+       info->vmdq_queue_num = 0;
+
+       info->default_rxportconf.burst_size = HNS3_DEFAULT_PORT_CONF_BURST_SIZE;
+       info->default_txportconf.burst_size = HNS3_DEFAULT_PORT_CONF_BURST_SIZE;
+       info->default_rxportconf.nb_queues = HNS3_DEFAULT_PORT_CONF_QUEUES_NUM;
+       info->default_txportconf.nb_queues = HNS3_DEFAULT_PORT_CONF_QUEUES_NUM;
+
+       return 0;
+}
+
+static int
+hns3_fw_version_get(struct rte_eth_dev *eth_dev, char *fw_version,
+                   size_t fw_size)
+{
+       struct hns3_adapter *hns = eth_dev->data->dev_private;
+       struct hns3_hw *hw = &hns->hw;
+       int ret;
+
+       ret = snprintf(fw_version, fw_size, "0x%08x", hw->fw_version);
+       ret += 1; /* add the size of '\0' */
+       if (fw_size < (uint32_t)ret)
+               return ret;
+       else
+               return 0;
+}
+
+static int
+hns3_dev_link_update(struct rte_eth_dev *eth_dev,
+                    __rte_unused int wait_to_complete)
+{
+       struct hns3_adapter *hns = eth_dev->data->dev_private;
+       struct hns3_hw *hw = &hns->hw;
+       struct hns3_mac *mac = &hw->mac;
+       struct rte_eth_link new_link;
+
+       memset(&new_link, 0, sizeof(new_link));
+       switch (mac->link_speed) {
+       case ETH_SPEED_NUM_10M:
+       case ETH_SPEED_NUM_100M:
+       case ETH_SPEED_NUM_1G:
+       case ETH_SPEED_NUM_10G:
+       case ETH_SPEED_NUM_25G:
+       case ETH_SPEED_NUM_40G:
+       case ETH_SPEED_NUM_50G:
+       case ETH_SPEED_NUM_100G:
+               new_link.link_speed = mac->link_speed;
+               break;
+       default:
+               new_link.link_speed = ETH_SPEED_NUM_100M;
+               break;
+       }
+
+       new_link.link_duplex = mac->link_duplex;
+       new_link.link_status = mac->link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
+       new_link.link_autoneg =
+           !(eth_dev->data->dev_conf.link_speeds & ETH_LINK_SPEED_FIXED);
+
+       return rte_eth_linkstatus_set(eth_dev, &new_link);
+}
+
 static int
 hns3_parse_func_status(struct hns3_hw *hw, struct hns3_func_status_cmd *status)
 {
@@ -1434,6 +2341,129 @@ hns3_set_promisc_mode(struct hns3_hw *hw, bool en_uc_pmc, bool en_mc_pmc)
        return 0;
 }
 
+static int
+hns3_get_sfp_speed(struct hns3_hw *hw, uint32_t *speed)
+{
+       struct hns3_sfp_speed_cmd *resp;
+       struct hns3_cmd_desc desc;
+       int ret;
+
+       hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_SFP_GET_SPEED, true);
+       resp = (struct hns3_sfp_speed_cmd *)desc.data;
+       ret = hns3_cmd_send(hw, &desc, 1);
+       if (ret == -EOPNOTSUPP) {
+               hns3_err(hw, "IMP do not support get SFP speed %d", ret);
+               return ret;
+       } else if (ret) {
+               hns3_err(hw, "get sfp speed failed %d", ret);
+               return ret;
+       }
+
+       *speed = resp->sfp_speed;
+
+       return 0;
+}
+
+static uint8_t
+hns3_check_speed_dup(uint8_t duplex, uint32_t speed)
+{
+       if (!(speed == ETH_SPEED_NUM_10M || speed == ETH_SPEED_NUM_100M))
+               duplex = ETH_LINK_FULL_DUPLEX;
+
+       return duplex;
+}
+
+static int
+hns3_cfg_mac_speed_dup(struct hns3_hw *hw, uint32_t speed, uint8_t duplex)
+{
+       struct hns3_mac *mac = &hw->mac;
+       int ret;
+
+       duplex = hns3_check_speed_dup(duplex, speed);
+       if (mac->link_speed == speed && mac->link_duplex == duplex)
+               return 0;
+
+       ret = hns3_cfg_mac_speed_dup_hw(hw, speed, duplex);
+       if (ret)
+               return ret;
+
+       mac->link_speed = speed;
+       mac->link_duplex = duplex;
+
+       return 0;
+}
+
+static int
+hns3_update_speed_duplex(struct rte_eth_dev *eth_dev)
+{
+       struct hns3_adapter *hns = eth_dev->data->dev_private;
+       struct hns3_hw *hw = &hns->hw;
+       struct hns3_pf *pf = &hns->pf;
+       uint32_t speed;
+       int ret;
+
+       /* If IMP do not support get SFP/qSFP speed, return directly */
+       if (!pf->support_sfp_query)
+               return 0;
+
+       ret = hns3_get_sfp_speed(hw, &speed);
+       if (ret == -EOPNOTSUPP) {
+               pf->support_sfp_query = false;
+               return ret;
+       } else if (ret)
+               return ret;
+
+       if (speed == ETH_SPEED_NUM_NONE)
+               return 0; /* do nothing if no SFP */
+
+       /* Config full duplex for SFP */
+       return hns3_cfg_mac_speed_dup(hw, speed, ETH_LINK_FULL_DUPLEX);
+}
+
+static int
+hns3_get_mac_link_status(struct hns3_hw *hw)
+{
+       struct hns3_link_status_cmd *req;
+       struct hns3_cmd_desc desc;
+       int link_status;
+       int ret;
+
+       hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_QUERY_LINK_STATUS, true);
+       ret = hns3_cmd_send(hw, &desc, 1);
+       if (ret) {
+               hns3_err(hw, "get link status cmd failed %d", ret);
+               return ret;
+       }
+
+       req = (struct hns3_link_status_cmd *)desc.data;
+       link_status = req->status & HNS3_LINK_STATUS_UP_M;
+
+       return !!link_status;
+}
+
+static void
+hns3_update_link_status(struct hns3_hw *hw)
+{
+       int state;
+
+       state = hns3_get_mac_link_status(hw);
+       if (state != hw->mac.link_status)
+               hw->mac.link_status = state;
+}
+
+static void
+hns3_service_handler(void *param)
+{
+       struct rte_eth_dev *eth_dev = (struct rte_eth_dev *)param;
+       struct hns3_adapter *hns = eth_dev->data->dev_private;
+       struct hns3_hw *hw = &hns->hw;
+
+       hns3_update_speed_duplex(eth_dev);
+       hns3_update_link_status(hw);
+
+       rte_eal_alarm_set(HNS3_SERVICE_INTERVAL, hns3_service_handler, eth_dev);
+}
+
 static int
 hns3_init_hardware(struct hns3_adapter *hns)
 {
@@ -1470,6 +2500,12 @@ hns3_init_hardware(struct hns3_adapter *hns)
                goto err_mac_init;
        }
 
+       ret = hns3_init_fd_config(hns);
+       if (ret) {
+               PMD_INIT_LOG(ERR, "Failed to init flow director: %d", ret);
+               goto err_mac_init;
+       }
+
        ret = hns3_config_tso(hw, HNS3_TSO_MSS_MIN, HNS3_TSO_MSS_MAX);
        if (ret) {
                PMD_INIT_LOG(ERR, "Failed to config tso: %d", ret);
@@ -1529,8 +2565,18 @@ hns3_init_pf(struct rte_eth_dev *eth_dev)
                goto err_get_config;
        }
 
+       /* Initialize flow director filter list & hash */
+       ret = hns3_fdir_filter_init(hns);
+       if (ret) {
+               PMD_INIT_LOG(ERR, "Failed to alloc hashmap for fdir: %d", ret);
+               goto err_hw_init;
+       }
+
        return 0;
 
+err_hw_init:
+       hns3_uninit_umv_space(hw);
+
 err_get_config:
        hns3_cmd_uninit(hw);
 
@@ -1551,6 +2597,7 @@ hns3_uninit_pf(struct rte_eth_dev *eth_dev)
 
        PMD_INIT_FUNC_TRACE();
 
+       hns3_fdir_filter_uninit(hns);
        hns3_uninit_umv_space(hw);
        hns3_cmd_uninit(hw);
        hns3_cmd_destroy_queue(hw);
@@ -1564,12 +2611,26 @@ hns3_dev_close(struct rte_eth_dev *eth_dev)
        struct hns3_hw *hw = &hns->hw;
 
        hw->adapter_state = HNS3_NIC_CLOSING;
+       rte_eal_alarm_cancel(hns3_service_handler, eth_dev);
+
+       hns3_configure_all_mc_mac_addr(hns, true);
        hns3_uninit_pf(eth_dev);
+       rte_free(eth_dev->process_private);
+       eth_dev->process_private = NULL;
        hw->adapter_state = HNS3_NIC_CLOSED;
 }
 
 static const struct eth_dev_ops hns3_eth_dev_ops = {
        .dev_close          = hns3_dev_close,
+       .mtu_set            = hns3_dev_mtu_set,
+       .dev_infos_get          = hns3_dev_infos_get,
+       .fw_version_get         = hns3_fw_version_get,
+       .mac_addr_add           = hns3_add_mac_addr,
+       .mac_addr_remove        = hns3_remove_mac_addr,
+       .mac_addr_set           = hns3_set_default_mac_addr,
+       .set_mc_addr_list       = hns3_set_mc_mac_addr_list,
+       .link_update            = hns3_dev_link_update,
+       .filter_ctrl            = hns3_dev_filter_ctrl,
 };
 
 static int
@@ -1580,6 +2641,16 @@ hns3_dev_init(struct rte_eth_dev *eth_dev)
        int ret;
 
        PMD_INIT_FUNC_TRACE();
+       eth_dev->process_private = (struct hns3_process_private *)
+           rte_zmalloc_socket("hns3_filter_list",
+                              sizeof(struct hns3_process_private),
+                              RTE_CACHE_LINE_SIZE, eth_dev->device->numa_node);
+       if (eth_dev->process_private == NULL) {
+               PMD_INIT_LOG(ERR, "Failed to alloc memory for process private");
+               return -ENOMEM;
+       }
+       /* initialize flow filter lists */
+       hns3_filterlist_init(eth_dev);
 
        eth_dev->dev_ops = &hns3_eth_dev_ops;
        if (rte_eal_process_type() != RTE_PROC_PRIMARY)
@@ -1624,6 +2695,7 @@ hns3_dev_init(struct rte_eth_dev *eth_dev)
         */
        eth_dev->data->dev_flags |= RTE_ETH_DEV_CLOSE_REMOVE;
 
+       rte_eal_alarm_set(HNS3_SERVICE_INTERVAL, hns3_service_handler, eth_dev);
        hns3_info(hw, "hns3 dev initialization successful!");
        return 0;
 
@@ -1635,6 +2707,8 @@ err_init_pf:
        eth_dev->rx_pkt_burst = NULL;
        eth_dev->tx_pkt_burst = NULL;
        eth_dev->tx_pkt_prepare = NULL;
+       rte_free(eth_dev->process_private);
+       eth_dev->process_private = NULL;
        return ret;
 }