From 7d7f9f80bbfb59368b81ba7e52286115754e23c0 Mon Sep 17 00:00:00 2001 From: "Wei Hu (Xavier)" Date: Thu, 26 Sep 2019 22:01:53 +0800 Subject: [PATCH] net/hns3: support MAC address related operations This patch adds the following mac address related operations defined in struct eth_dev_ops: mac_addr_add, mac_addr_remove, mac_addr_set and set_mc_addr_list. Signed-off-by: Wei Hu (Xavier) Signed-off-by: Chunsong Feng Signed-off-by: Min Hu (Connor) Signed-off-by: Hao Chen Signed-off-by: Huisong Li Reviewed-by: Ferruh Yigit --- doc/guides/nics/features/hns3.ini | 2 + drivers/net/hns3/hns3_ethdev.c | 772 ++++++++++++++++++++++++++++++ 2 files changed, 774 insertions(+) diff --git a/doc/guides/nics/features/hns3.ini b/doc/guides/nics/features/hns3.ini index 2eb57c8ced..43ade8c62d 100644 --- a/doc/guides/nics/features/hns3.ini +++ b/doc/guides/nics/features/hns3.ini @@ -4,6 +4,8 @@ ; Refer to default.ini for the full list of available PMD features. ; [Features] +Unicast MAC filter = Y +Multicast MAC filter = Y Linux UIO = Y Linux VFIO = Y ARMv8 = Y diff --git a/drivers/net/hns3/hns3_ethdev.c b/drivers/net/hns3/hns3_ethdev.c index f0a2e36e5e..25b2720cc1 100644 --- a/drivers/net/hns3/hns3_ethdev.c +++ b/drivers/net/hns3/hns3_ethdev.c @@ -143,6 +143,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) { @@ -1564,12 +2330,18 @@ hns3_dev_close(struct rte_eth_dev *eth_dev) struct hns3_hw *hw = &hns->hw; hw->adapter_state = HNS3_NIC_CLOSING; + + hns3_configure_all_mc_mac_addr(hns, true); hns3_uninit_pf(eth_dev); hw->adapter_state = HNS3_NIC_CLOSED; } static const struct eth_dev_ops hns3_eth_dev_ops = { .dev_close = hns3_dev_close, + .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, }; static int -- 2.20.1