diff options
author | Anirudh Venkataramanan <anirudh.venkataramanan@intel.com> | 2018-08-09 06:29:49 -0700 |
---|---|---|
committer | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2018-08-28 10:29:38 -0700 |
commit | 80d144c9ac82fd07436c02c830b2af03c471db8a (patch) | |
tree | 2618a8395c0f7a6148c06de9ccc0e49b2fd6e8e5 /drivers/net | |
parent | 74118f7af092a4edc194573a8749f3843d280d3f (diff) |
ice: Refactor switch rule management structures and functions
This patch is an adaptation of the work originally done by Grishma
Kotecha <grishma.kotecha@intel.com> that in summary refactors the
switch filtering logic in the driver. More specifically,
- Update the recipe structure to also store list of rules
- Update the existing code for recipes like MAC, VLAN, ethtype etc to
use list head that is attached to switch recipe structure
- Add a common function to search for a rule entry and add a new rule
entry. Update the code to use this new function.
- Refactor the rem_handle_vsi_list function to simplify the logic
CC: Shannon Nelson <shannon.nelson@oracle.com>
Signed-off-by: Anirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: Tony Brelinski <tonyx.brelinski@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/ethernet/intel/ice/ice_adminq_cmd.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/ice/ice_common.c | 36 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/ice/ice_switch.c | 967 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/ice/ice_switch.h | 35 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/ice/ice_type.h | 13 |
5 files changed, 500 insertions, 553 deletions
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h index 9a33fb95c0ea..87b304db9cad 100644 --- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h +++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h @@ -443,6 +443,8 @@ struct ice_aqc_vsi_props { u8 reserved[24]; }; +#define ICE_MAX_NUM_RECIPES 64 + /* Add/Update/Remove/Get switch rules (indirect 0x02A0, 0x02A1, 0x02A2, 0x02A3) */ struct ice_aqc_sw_rules { diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c index 2a1e13576ce2..4c6b1038dc5f 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.c +++ b/drivers/net/ethernet/intel/ice/ice_common.c @@ -388,20 +388,7 @@ static enum ice_status ice_init_fltr_mgmt_struct(struct ice_hw *hw) INIT_LIST_HEAD(&sw->vsi_list_map_head); - mutex_init(&sw->mac_list_lock); - INIT_LIST_HEAD(&sw->mac_list_head); - - mutex_init(&sw->vlan_list_lock); - INIT_LIST_HEAD(&sw->vlan_list_head); - - mutex_init(&sw->eth_m_list_lock); - INIT_LIST_HEAD(&sw->eth_m_list_head); - - mutex_init(&sw->promisc_list_lock); - INIT_LIST_HEAD(&sw->promisc_list_head); - - mutex_init(&sw->mac_vlan_list_lock); - INIT_LIST_HEAD(&sw->mac_vlan_list_head); + ice_init_def_sw_recp(hw); return 0; } @@ -415,19 +402,28 @@ static void ice_cleanup_fltr_mgmt_struct(struct ice_hw *hw) struct ice_switch_info *sw = hw->switch_info; struct ice_vsi_list_map_info *v_pos_map; struct ice_vsi_list_map_info *v_tmp_map; + struct ice_sw_recipe *recps; + u8 i; list_for_each_entry_safe(v_pos_map, v_tmp_map, &sw->vsi_list_map_head, list_entry) { list_del(&v_pos_map->list_entry); devm_kfree(ice_hw_to_dev(hw), v_pos_map); } + recps = hw->switch_info->recp_list; + for (i = 0; i < ICE_SW_LKUP_LAST; i++) { + struct ice_fltr_mgmt_list_entry *lst_itr, *tmp_entry; + + recps[i].root_rid = i; + mutex_destroy(&recps[i].filt_rule_lock); + list_for_each_entry_safe(lst_itr, tmp_entry, + &recps[i].filt_rules, list_entry) { + list_del(&lst_itr->list_entry); + devm_kfree(ice_hw_to_dev(hw), lst_itr); + } + } - mutex_destroy(&sw->mac_list_lock); - mutex_destroy(&sw->vlan_list_lock); - mutex_destroy(&sw->eth_m_list_lock); - mutex_destroy(&sw->promisc_list_lock); - mutex_destroy(&sw->mac_vlan_list_lock); - + devm_kfree(ice_hw_to_dev(hw), sw->recp_list); devm_kfree(ice_hw_to_dev(hw), sw); } diff --git a/drivers/net/ethernet/intel/ice/ice_switch.c b/drivers/net/ethernet/intel/ice/ice_switch.c index d8b18cabc3a8..2693bebef977 100644 --- a/drivers/net/ethernet/intel/ice/ice_switch.c +++ b/drivers/net/ethernet/intel/ice/ice_switch.c @@ -86,6 +86,35 @@ ice_aq_alloc_free_res(struct ice_hw *hw, u16 num_entries, } /** + * ice_init_def_sw_recp - initialize the recipe book keeping tables + * @hw: pointer to the hw struct + * + * Allocate memory for the entire recipe table and initialize the structures/ + * entries corresponding to basic recipes. + */ +enum ice_status +ice_init_def_sw_recp(struct ice_hw *hw) +{ + struct ice_sw_recipe *recps; + u8 i; + + recps = devm_kcalloc(ice_hw_to_dev(hw), ICE_MAX_NUM_RECIPES, + sizeof(struct ice_sw_recipe), GFP_KERNEL); + if (!recps) + return ICE_ERR_NO_MEMORY; + + for (i = 0; i < ICE_SW_LKUP_LAST; i++) { + recps[i].root_rid = i; + INIT_LIST_HEAD(&recps[i].filt_rules); + mutex_init(&recps[i].filt_rule_lock); + } + + hw->switch_info->recp_list = recps; + + return 0; +} + +/** * ice_aq_get_sw_cfg - get switch configuration * @hw: pointer to the hardware structure * @buf: pointer to the result buffer @@ -818,10 +847,10 @@ static enum ice_status ice_create_pkt_fwd_rule(struct ice_hw *hw, struct ice_fltr_list_entry *f_entry) { - struct ice_switch_info *sw = hw->switch_info; struct ice_fltr_mgmt_list_entry *fm_entry; struct ice_aqc_sw_rules_elem *s_rule; enum ice_sw_lkup_type l_type; + struct ice_sw_recipe *recp; enum ice_status status; s_rule = devm_kzalloc(ice_hw_to_dev(hw), @@ -862,31 +891,9 @@ ice_create_pkt_fwd_rule(struct ice_hw *hw, * calls remove filter AQ command */ l_type = fm_entry->fltr_info.lkup_type; - if (l_type == ICE_SW_LKUP_MAC) { - mutex_lock(&sw->mac_list_lock); - list_add(&fm_entry->list_entry, &sw->mac_list_head); - mutex_unlock(&sw->mac_list_lock); - } else if (l_type == ICE_SW_LKUP_VLAN) { - mutex_lock(&sw->vlan_list_lock); - list_add(&fm_entry->list_entry, &sw->vlan_list_head); - mutex_unlock(&sw->vlan_list_lock); - } else if (l_type == ICE_SW_LKUP_ETHERTYPE || - l_type == ICE_SW_LKUP_ETHERTYPE_MAC) { - mutex_lock(&sw->eth_m_list_lock); - list_add(&fm_entry->list_entry, &sw->eth_m_list_head); - mutex_unlock(&sw->eth_m_list_lock); - } else if (l_type == ICE_SW_LKUP_PROMISC || - l_type == ICE_SW_LKUP_PROMISC_VLAN) { - mutex_lock(&sw->promisc_list_lock); - list_add(&fm_entry->list_entry, &sw->promisc_list_head); - mutex_unlock(&sw->promisc_list_lock); - } else if (fm_entry->fltr_info.lkup_type == ICE_SW_LKUP_MAC_VLAN) { - mutex_lock(&sw->mac_vlan_list_lock); - list_add(&fm_entry->list_entry, &sw->mac_vlan_list_head); - mutex_unlock(&sw->mac_vlan_list_lock); - } else { - status = ICE_ERR_NOT_IMPL; - } + recp = &hw->switch_info->recp_list[l_type]; + list_add(&fm_entry->list_entry, &recp->filt_rules); + ice_create_pkt_fwd_rule_exit: devm_kfree(ice_hw_to_dev(hw), s_rule); return status; @@ -895,19 +902,15 @@ ice_create_pkt_fwd_rule_exit: /** * ice_update_pkt_fwd_rule * @hw: pointer to the hardware structure - * @rule_id: rule of previously created switch rule to update - * @vsi_list_id: VSI list id to be updated with - * @f_info: ice_fltr_info to pull other information for switch rule + * @f_info: filter information for switch rule * * Call AQ command to update a previously created switch rule with a * VSI list id */ static enum ice_status -ice_update_pkt_fwd_rule(struct ice_hw *hw, u16 rule_id, u16 vsi_list_id, - struct ice_fltr_info f_info) +ice_update_pkt_fwd_rule(struct ice_hw *hw, struct ice_fltr_info *f_info) { struct ice_aqc_sw_rules_elem *s_rule; - struct ice_fltr_info tmp_fltr; enum ice_status status; s_rule = devm_kzalloc(ice_hw_to_dev(hw), @@ -915,14 +918,9 @@ ice_update_pkt_fwd_rule(struct ice_hw *hw, u16 rule_id, u16 vsi_list_id, if (!s_rule) return ICE_ERR_NO_MEMORY; - tmp_fltr = f_info; - tmp_fltr.fltr_act = ICE_FWD_TO_VSI_LIST; - tmp_fltr.fwd_id.vsi_list_id = vsi_list_id; - - ice_fill_sw_rule(hw, &tmp_fltr, s_rule, - ice_aqc_opc_update_sw_rules); + ice_fill_sw_rule(hw, f_info, s_rule, ice_aqc_opc_update_sw_rules); - s_rule->pdata.lkup_tx_rx.index = cpu_to_le16(rule_id); + s_rule->pdata.lkup_tx_rx.index = cpu_to_le16(f_info->fltr_rule_id); /* Update switch rule with new rule set to forward VSI list */ status = ice_aq_sw_rules(hw, s_rule, ICE_SW_RULE_RX_TX_ETH_HDR_SIZE, 1, @@ -933,7 +931,7 @@ ice_update_pkt_fwd_rule(struct ice_hw *hw, u16 rule_id, u16 vsi_list_id, } /** - * ice_handle_vsi_list_mgmt + * ice_add_update_vsi_list * @hw: pointer to the hardware structure * @m_entry: pointer to current filter management list entry * @cur_fltr: filter information from the book keeping entry @@ -954,10 +952,10 @@ ice_update_pkt_fwd_rule(struct ice_hw *hw, u16 rule_id, u16 vsi_list_id, * using the update switch rule command */ static enum ice_status -ice_handle_vsi_list_mgmt(struct ice_hw *hw, - struct ice_fltr_mgmt_list_entry *m_entry, - struct ice_fltr_info *cur_fltr, - struct ice_fltr_info *new_fltr) +ice_add_update_vsi_list(struct ice_hw *hw, + struct ice_fltr_mgmt_list_entry *m_entry, + struct ice_fltr_info *cur_fltr, + struct ice_fltr_info *new_fltr) { enum ice_status status = 0; u16 vsi_list_id = 0; @@ -977,8 +975,8 @@ ice_handle_vsi_list_mgmt(struct ice_hw *hw, * a part of a VSI list. So, create a VSI list with the old and * new VSIs. */ + struct ice_fltr_info tmp_fltr; u16 vsi_id_arr[2]; - u16 fltr_rule; /* A rule already exists with the new VSI being added */ if (cur_fltr->fwd_id.vsi_id == new_fltr->fwd_id.vsi_id) @@ -992,12 +990,14 @@ ice_handle_vsi_list_mgmt(struct ice_hw *hw, if (status) return status; - fltr_rule = cur_fltr->fltr_rule_id; + tmp_fltr = *new_fltr; + tmp_fltr.fltr_rule_id = cur_fltr->fltr_rule_id; + tmp_fltr.fltr_act = ICE_FWD_TO_VSI_LIST; + tmp_fltr.fwd_id.vsi_list_id = vsi_list_id; /* Update the previous switch rule of "MAC forward to VSI" to * "MAC fwd to VSI list" */ - status = ice_update_pkt_fwd_rule(hw, fltr_rule, vsi_list_id, - *new_fltr); + status = ice_update_pkt_fwd_rule(hw, &tmp_fltr); if (status) return status; @@ -1042,54 +1042,245 @@ ice_handle_vsi_list_mgmt(struct ice_hw *hw, } /** - * ice_find_mac_entry + * ice_find_rule_entry - Search a rule entry * @hw: pointer to the hardware structure - * @mac_addr: MAC address to search for + * @recp_id: lookup type for which the specified rule needs to be searched + * @f_info: rule information * - * Helper function to search for a MAC entry using a given MAC address - * Returns pointer to the entry if found. + * Helper function to search for a given rule entry + * Returns pointer to entry storing the rule if found */ static struct ice_fltr_mgmt_list_entry * -ice_find_mac_entry(struct ice_hw *hw, u8 *mac_addr) +ice_find_rule_entry(struct ice_hw *hw, u8 recp_id, struct ice_fltr_info *f_info) { - struct ice_fltr_mgmt_list_entry *m_list_itr, *mac_ret = NULL; + struct ice_fltr_mgmt_list_entry *list_itr, *ret = NULL; struct ice_switch_info *sw = hw->switch_info; - - mutex_lock(&sw->mac_list_lock); - list_for_each_entry(m_list_itr, &sw->mac_list_head, list_entry) { - u8 *buf = &m_list_itr->fltr_info.l_data.mac.mac_addr[0]; - - if (ether_addr_equal(buf, mac_addr)) { - mac_ret = m_list_itr; + struct list_head *list_head; + + list_head = &sw->recp_list[recp_id].filt_rules; + list_for_each_entry(list_itr, list_head, list_entry) { + if (!memcmp(&f_info->l_data, &list_itr->fltr_info.l_data, + sizeof(f_info->l_data)) && + f_info->flag == list_itr->fltr_info.flag) { + ret = list_itr; break; } } - mutex_unlock(&sw->mac_list_lock); - return mac_ret; + return ret; } /** - * ice_add_shared_mac - Add one MAC shared filter rule + * ice_add_rule_internal - add rule for a given lookup type * @hw: pointer to the hardware structure + * @recp_id: lookup type (recipe id) for which rule has to be added * @f_entry: structure containing MAC forwarding information * - * Adds or updates the book keeping list for the MAC addresses + * Adds or updates the rule lists for a given recipe */ static enum ice_status -ice_add_shared_mac(struct ice_hw *hw, struct ice_fltr_list_entry *f_entry) +ice_add_rule_internal(struct ice_hw *hw, u8 recp_id, + struct ice_fltr_list_entry *f_entry) { + struct ice_switch_info *sw = hw->switch_info; struct ice_fltr_info *new_fltr, *cur_fltr; struct ice_fltr_mgmt_list_entry *m_entry; + struct mutex *rule_lock; /* Lock to protect filter rule list */ + enum ice_status status = 0; - new_fltr = &f_entry->fltr_info; + rule_lock = &sw->recp_list[recp_id].filt_rule_lock; - m_entry = ice_find_mac_entry(hw, &new_fltr->l_data.mac.mac_addr[0]); - if (!m_entry) + mutex_lock(rule_lock); + new_fltr = &f_entry->fltr_info; + if (new_fltr->flag & ICE_FLTR_RX) + new_fltr->src = hw->port_info->lport; + else if (new_fltr->flag & ICE_FLTR_TX) + new_fltr->src = f_entry->fltr_info.fwd_id.vsi_id; + + m_entry = ice_find_rule_entry(hw, recp_id, new_fltr); + if (!m_entry) { + mutex_unlock(rule_lock); return ice_create_pkt_fwd_rule(hw, f_entry); + } cur_fltr = &m_entry->fltr_info; + status = ice_add_update_vsi_list(hw, m_entry, cur_fltr, new_fltr); + mutex_unlock(rule_lock); + + return status; +} + +/** + * ice_remove_vsi_list_rule + * @hw: pointer to the hardware structure + * @vsi_list_id: VSI list id generated as part of allocate resource + * @lkup_type: switch rule filter lookup type + * + * The VSI list should be emptied before this function is called to remove the + * VSI list. + */ +static enum ice_status +ice_remove_vsi_list_rule(struct ice_hw *hw, u16 vsi_list_id, + enum ice_sw_lkup_type lkup_type) +{ + struct ice_aqc_sw_rules_elem *s_rule; + enum ice_status status; + u16 s_rule_size; + + s_rule_size = (u16)ICE_SW_RULE_VSI_LIST_SIZE(0); + s_rule = devm_kzalloc(ice_hw_to_dev(hw), s_rule_size, GFP_KERNEL); + if (!s_rule) + return ICE_ERR_NO_MEMORY; + + s_rule->type = cpu_to_le16(ICE_AQC_SW_RULES_T_VSI_LIST_CLEAR); + s_rule->pdata.vsi_list.index = cpu_to_le16(vsi_list_id); + + /* Free the vsi_list resource that we allocated. It is assumed that the + * list is empty at this point. + */ + status = ice_aq_alloc_free_vsi_list(hw, &vsi_list_id, lkup_type, + ice_aqc_opc_free_res); - return ice_handle_vsi_list_mgmt(hw, m_entry, cur_fltr, new_fltr); + devm_kfree(ice_hw_to_dev(hw), s_rule); + return status; +} + +/** + * ice_rem_update_vsi_list + * @hw: pointer to the hardware structure + * @vsi_id: ID of the VSI to remove + * @fm_list: filter management entry for which the VSI list management needs to + * be done + */ +static enum ice_status +ice_rem_update_vsi_list(struct ice_hw *hw, u16 vsi_id, + struct ice_fltr_mgmt_list_entry *fm_list) +{ + enum ice_sw_lkup_type lkup_type; + enum ice_status status = 0; + u16 vsi_list_id; + + if (fm_list->fltr_info.fltr_act != ICE_FWD_TO_VSI_LIST || + fm_list->vsi_count == 0) + return ICE_ERR_PARAM; + + /* A rule with the VSI being removed does not exist */ + if (!test_bit(vsi_id, fm_list->vsi_list_info->vsi_map)) + return ICE_ERR_DOES_NOT_EXIST; + + lkup_type = fm_list->fltr_info.lkup_type; + vsi_list_id = fm_list->fltr_info.fwd_id.vsi_list_id; + + status = ice_update_vsi_list_rule(hw, &vsi_id, 1, vsi_list_id, true, + ice_aqc_opc_update_sw_rules, + lkup_type); + if (status) + return status; + + fm_list->vsi_count--; + clear_bit(vsi_id, fm_list->vsi_list_info->vsi_map); + + if ((fm_list->vsi_count == 1 && lkup_type != ICE_SW_LKUP_VLAN) || + (fm_list->vsi_count == 0 && lkup_type == ICE_SW_LKUP_VLAN)) { + struct ice_vsi_list_map_info *vsi_list_info = + fm_list->vsi_list_info; + u16 rem_vsi_id; + + rem_vsi_id = find_first_bit(vsi_list_info->vsi_map, + ICE_MAX_VSI); + if (rem_vsi_id == ICE_MAX_VSI) + return ICE_ERR_OUT_OF_RANGE; + + status = ice_update_vsi_list_rule(hw, &rem_vsi_id, 1, + vsi_list_id, true, + ice_aqc_opc_update_sw_rules, + lkup_type); + if (status) + return status; + + /* Remove the VSI list since it is no longer used */ + status = ice_remove_vsi_list_rule(hw, vsi_list_id, lkup_type); + if (status) + return status; + + /* Change the list entry action from VSI_LIST to VSI */ + fm_list->fltr_info.fltr_act = ICE_FWD_TO_VSI; + fm_list->fltr_info.fwd_id.vsi_id = rem_vsi_id; + + list_del(&vsi_list_info->list_entry); + devm_kfree(ice_hw_to_dev(hw), vsi_list_info); + fm_list->vsi_list_info = NULL; + } + + return status; +} + +/** + * ice_remove_rule_internal - Remove a filter rule of a given type + * @hw: pointer to the hardware structure + * @recp_id: recipe id for which the rule needs to removed + * @f_entry: rule entry containing filter information + */ +static enum ice_status +ice_remove_rule_internal(struct ice_hw *hw, u8 recp_id, + struct ice_fltr_list_entry *f_entry) +{ + struct ice_switch_info *sw = hw->switch_info; + struct ice_fltr_mgmt_list_entry *list_elem; + struct mutex *rule_lock; /* Lock to protect filter rule list */ + enum ice_status status = 0; + bool remove_rule = false; + u16 vsi_id; + + rule_lock = &sw->recp_list[recp_id].filt_rule_lock; + mutex_lock(rule_lock); + list_elem = ice_find_rule_entry(hw, recp_id, &f_entry->fltr_info); + if (!list_elem) { + status = ICE_ERR_DOES_NOT_EXIST; + goto exit; + } + + if (list_elem->fltr_info.fltr_act != ICE_FWD_TO_VSI_LIST) { + remove_rule = true; + } else { + vsi_id = f_entry->fltr_info.fwd_id.vsi_id; + status = ice_rem_update_vsi_list(hw, vsi_id, list_elem); + if (status) + goto exit; + /* if vsi count goes to zero after updating the vsi list */ + if (list_elem->vsi_count == 0) + remove_rule = true; + } + + if (remove_rule) { + /* Remove the lookup rule */ + struct ice_aqc_sw_rules_elem *s_rule; + + s_rule = devm_kzalloc(ice_hw_to_dev(hw), + ICE_SW_RULE_RX_TX_NO_HDR_SIZE, + GFP_KERNEL); + if (!s_rule) { + status = ICE_ERR_NO_MEMORY; + goto exit; + } + + ice_fill_sw_rule(hw, &list_elem->fltr_info, s_rule, + ice_aqc_opc_remove_sw_rules); + + status = ice_aq_sw_rules(hw, s_rule, + ICE_SW_RULE_RX_TX_NO_HDR_SIZE, 1, + ice_aqc_opc_remove_sw_rules, NULL); + if (status) + goto exit; + + /* Remove a book keeping from the list */ + devm_kfree(ice_hw_to_dev(hw), s_rule); + + list_del(&list_elem->list_entry); + devm_kfree(ice_hw_to_dev(hw), list_elem); + } +exit: + mutex_unlock(rule_lock); + return status; } /** @@ -1108,7 +1299,10 @@ ice_add_mac(struct ice_hw *hw, struct list_head *m_list) { struct ice_aqc_sw_rules_elem *s_rule, *r_iter; struct ice_fltr_list_entry *m_list_itr; + struct list_head *rule_head; u16 elem_sent, total_elem_left; + struct ice_switch_info *sw; + struct mutex *rule_lock; /* Lock to protect filter rule list */ enum ice_status status = 0; u16 num_unicast = 0; u16 s_rule_size; @@ -1116,48 +1310,62 @@ ice_add_mac(struct ice_hw *hw, struct list_head *m_list) if (!m_list || !hw) return ICE_ERR_PARAM; + s_rule = NULL; + sw = hw->switch_info; + rule_lock = &sw->recp_list[ICE_SW_LKUP_MAC].filt_rule_lock; list_for_each_entry(m_list_itr, m_list, list_entry) { u8 *add = &m_list_itr->fltr_info.l_data.mac.mac_addr[0]; - if (m_list_itr->fltr_info.lkup_type != ICE_SW_LKUP_MAC) - return ICE_ERR_PARAM; - if (is_zero_ether_addr(add)) + m_list_itr->fltr_info.flag = ICE_FLTR_TX; + if (m_list_itr->fltr_info.lkup_type != ICE_SW_LKUP_MAC || + is_zero_ether_addr(add)) return ICE_ERR_PARAM; if (is_unicast_ether_addr(add) && !hw->ucast_shared) { /* Don't overwrite the unicast address */ - if (ice_find_mac_entry(hw, add)) + mutex_lock(rule_lock); + if (ice_find_rule_entry(hw, ICE_SW_LKUP_MAC, + &m_list_itr->fltr_info)) { + mutex_unlock(rule_lock); return ICE_ERR_ALREADY_EXISTS; + } + mutex_unlock(rule_lock); num_unicast++; } else if (is_multicast_ether_addr(add) || (is_unicast_ether_addr(add) && hw->ucast_shared)) { - status = ice_add_shared_mac(hw, m_list_itr); - if (status) { - m_list_itr->status = ICE_FLTR_STATUS_FW_FAIL; - return status; - } - m_list_itr->status = ICE_FLTR_STATUS_FW_SUCCESS; + m_list_itr->status = + ice_add_rule_internal(hw, ICE_SW_LKUP_MAC, + m_list_itr); + if (m_list_itr->status) + return m_list_itr->status; } } + mutex_lock(rule_lock); /* Exit if no suitable entries were found for adding bulk switch rule */ - if (!num_unicast) - return 0; + if (!num_unicast) { + status = 0; + goto ice_add_mac_exit; + } + + rule_head = &sw->recp_list[ICE_SW_LKUP_MAC].filt_rules; /* Allocate switch rule buffer for the bulk update for unicast */ s_rule_size = ICE_SW_RULE_RX_TX_ETH_HDR_SIZE; s_rule = devm_kcalloc(ice_hw_to_dev(hw), num_unicast, s_rule_size, GFP_KERNEL); - if (!s_rule) - return ICE_ERR_NO_MEMORY; + if (!s_rule) { + status = ICE_ERR_NO_MEMORY; + goto ice_add_mac_exit; + } r_iter = s_rule; list_for_each_entry(m_list_itr, m_list, list_entry) { struct ice_fltr_info *f_info = &m_list_itr->fltr_info; - u8 *addr = &f_info->l_data.mac.mac_addr[0]; + u8 *mac_addr = &f_info->l_data.mac.mac_addr[0]; - if (is_unicast_ether_addr(addr)) { - ice_fill_sw_rule(hw, &m_list_itr->fltr_info, - r_iter, ice_aqc_opc_add_sw_rules); + if (is_unicast_ether_addr(mac_addr)) { + ice_fill_sw_rule(hw, &m_list_itr->fltr_info, r_iter, + ice_aqc_opc_add_sw_rules); r_iter = (struct ice_aqc_sw_rules_elem *) ((u8 *)r_iter + s_rule_size); } @@ -1185,11 +1393,10 @@ ice_add_mac(struct ice_hw *hw, struct list_head *m_list) r_iter = s_rule; list_for_each_entry(m_list_itr, m_list, list_entry) { struct ice_fltr_info *f_info = &m_list_itr->fltr_info; - u8 *addr = &f_info->l_data.mac.mac_addr[0]; - struct ice_switch_info *sw = hw->switch_info; + u8 *mac_addr = &f_info->l_data.mac.mac_addr[0]; struct ice_fltr_mgmt_list_entry *fm_entry; - if (is_unicast_ether_addr(addr)) { + if (is_unicast_ether_addr(mac_addr)) { f_info->fltr_rule_id = le16_to_cpu(r_iter->pdata.lkup_tx_rx.index); f_info->fltr_act = ICE_FWD_TO_VSI; @@ -1205,46 +1412,21 @@ ice_add_mac(struct ice_hw *hw, struct list_head *m_list) /* The book keeping entries will get removed when * base driver calls remove filter AQ command */ - mutex_lock(&sw->mac_list_lock); - list_add(&fm_entry->list_entry, &sw->mac_list_head); - mutex_unlock(&sw->mac_list_lock); + list_add(&fm_entry->list_entry, rule_head); r_iter = (struct ice_aqc_sw_rules_elem *) ((u8 *)r_iter + s_rule_size); } } ice_add_mac_exit: - devm_kfree(ice_hw_to_dev(hw), s_rule); + mutex_unlock(rule_lock); + if (s_rule) + devm_kfree(ice_hw_to_dev(hw), s_rule); return status; } /** - * ice_find_vlan_entry - * @hw: pointer to the hardware structure - * @vlan_id: VLAN id to search for - * - * Helper function to search for a VLAN entry using a given VLAN id - * Returns pointer to the entry if found. - */ -static struct ice_fltr_mgmt_list_entry * -ice_find_vlan_entry(struct ice_hw *hw, u16 vlan_id) -{ - struct ice_fltr_mgmt_list_entry *vlan_list_itr, *vlan_ret = NULL; - struct ice_switch_info *sw = hw->switch_info; - - mutex_lock(&sw->vlan_list_lock); - list_for_each_entry(vlan_list_itr, &sw->vlan_list_head, list_entry) - if (vlan_list_itr->fltr_info.l_data.vlan.vlan_id == vlan_id) { - vlan_ret = vlan_list_itr; - break; - } - - mutex_unlock(&sw->vlan_list_lock); - return vlan_ret; -} - -/** * ice_add_vlan_internal - Add one VLAN based filter rule * @hw: pointer to the hardware structure * @f_entry: filter entry containing one VLAN information @@ -1252,20 +1434,22 @@ ice_find_vlan_entry(struct ice_hw *hw, u16 vlan_id) static enum ice_status ice_add_vlan_internal(struct ice_hw *hw, struct ice_fltr_list_entry *f_entry) { + struct ice_switch_info *sw = hw->switch_info; struct ice_fltr_info *new_fltr, *cur_fltr; struct ice_fltr_mgmt_list_entry *v_list_itr; - u16 vlan_id; + struct mutex *rule_lock; /* Lock to protect filter rule list */ + enum ice_status status = 0; new_fltr = &f_entry->fltr_info; /* VLAN id should only be 12 bits */ if (new_fltr->l_data.vlan.vlan_id > ICE_MAX_VLAN_ID) return ICE_ERR_PARAM; - vlan_id = new_fltr->l_data.vlan.vlan_id; - v_list_itr = ice_find_vlan_entry(hw, vlan_id); + rule_lock = &sw->recp_list[ICE_SW_LKUP_VLAN].filt_rule_lock; + mutex_lock(rule_lock); + v_list_itr = ice_find_rule_entry(hw, ICE_SW_LKUP_VLAN, new_fltr); if (!v_list_itr) { u16 vsi_id = ICE_VSI_INVAL_ID; - enum ice_status status; u16 vsi_list_id = 0; if (new_fltr->fltr_act == ICE_FWD_TO_VSI) { @@ -1279,26 +1463,33 @@ ice_add_vlan_internal(struct ice_hw *hw, struct ice_fltr_list_entry *f_entry) &vsi_list_id, lkup_type); if (status) - return status; + goto exit; new_fltr->fltr_act = ICE_FWD_TO_VSI_LIST; new_fltr->fwd_id.vsi_list_id = vsi_list_id; } status = ice_create_pkt_fwd_rule(hw, f_entry); if (!status && vsi_id != ICE_VSI_INVAL_ID) { - v_list_itr = ice_find_vlan_entry(hw, vlan_id); - if (!v_list_itr) - return ICE_ERR_DOES_NOT_EXIST; + v_list_itr = ice_find_rule_entry(hw, ICE_SW_LKUP_VLAN, + new_fltr); + if (!v_list_itr) { + status = ICE_ERR_DOES_NOT_EXIST; + goto exit; + } v_list_itr->vsi_list_info = ice_create_vsi_list_map(hw, &vsi_id, 1, vsi_list_id); } - return status; + goto exit; } cur_fltr = &v_list_itr->fltr_info; - return ice_handle_vsi_list_mgmt(hw, v_list_itr, cur_fltr, new_fltr); + status = ice_add_update_vsi_list(hw, v_list_itr, cur_fltr, new_fltr); + +exit: + mutex_unlock(rule_lock); + return status; } /** @@ -1315,326 +1506,25 @@ ice_add_vlan(struct ice_hw *hw, struct list_head *v_list) return ICE_ERR_PARAM; list_for_each_entry(v_list_itr, v_list, list_entry) { - enum ice_status status; - if (v_list_itr->fltr_info.lkup_type != ICE_SW_LKUP_VLAN) return ICE_ERR_PARAM; - - status = ice_add_vlan_internal(hw, v_list_itr); - if (status) { - v_list_itr->status = ICE_FLTR_STATUS_FW_FAIL; - return status; - } - v_list_itr->status = ICE_FLTR_STATUS_FW_SUCCESS; + v_list_itr->fltr_info.flag = ICE_FLTR_TX; + v_list_itr->status = ice_add_vlan_internal(hw, v_list_itr); + if (v_list_itr->status) + return v_list_itr->status; } return 0; } /** - * ice_remove_vsi_list_rule - * @hw: pointer to the hardware structure - * @vsi_list_id: VSI list id generated as part of allocate resource - * @lkup_type: switch rule filter lookup type - */ -static enum ice_status -ice_remove_vsi_list_rule(struct ice_hw *hw, u16 vsi_list_id, - enum ice_sw_lkup_type lkup_type) -{ - struct ice_aqc_sw_rules_elem *s_rule; - enum ice_status status; - u16 s_rule_size; - - s_rule_size = (u16)ICE_SW_RULE_VSI_LIST_SIZE(0); - s_rule = devm_kzalloc(ice_hw_to_dev(hw), s_rule_size, GFP_KERNEL); - if (!s_rule) - return ICE_ERR_NO_MEMORY; - - s_rule->type = cpu_to_le16(ICE_AQC_SW_RULES_T_VSI_LIST_CLEAR); - s_rule->pdata.vsi_list.index = cpu_to_le16(vsi_list_id); - /* FW expects number of VSIs in vsi_list resource to be 0 for clear - * command. Since memory is zero'ed out during initialization, it's not - * necessary to explicitly initialize the variable to 0. - */ - - status = ice_aq_sw_rules(hw, s_rule, s_rule_size, 1, - ice_aqc_opc_remove_sw_rules, NULL); - if (!status) - /* Free the vsi_list resource that we allocated */ - status = ice_aq_alloc_free_vsi_list(hw, &vsi_list_id, lkup_type, - ice_aqc_opc_free_res); - - devm_kfree(ice_hw_to_dev(hw), s_rule); - return status; -} - -/** - * ice_handle_rem_vsi_list_mgmt - * @hw: pointer to the hardware structure - * @vsi_id: ID of the VSI to remove - * @fm_list_itr: filter management entry for which the VSI list management - * needs to be done - */ -static enum ice_status -ice_handle_rem_vsi_list_mgmt(struct ice_hw *hw, u16 vsi_id, - struct ice_fltr_mgmt_list_entry *fm_list_itr) -{ - struct ice_switch_info *sw = hw->switch_info; - enum ice_status status = 0; - enum ice_sw_lkup_type lkup_type; - bool is_last_elem = true; - bool conv_list = false; - bool del_list = false; - u16 vsi_list_id; - - lkup_type = fm_list_itr->fltr_info.lkup_type; - vsi_list_id = fm_list_itr->fltr_info.fwd_id.vsi_list_id; - - if (fm_list_itr->vsi_count > 1) { - status = ice_update_vsi_list_rule(hw, &vsi_id, 1, vsi_list_id, - true, - ice_aqc_opc_update_sw_rules, - lkup_type); - if (status) - return status; - fm_list_itr->vsi_count--; - is_last_elem = false; - clear_bit(vsi_id, fm_list_itr->vsi_list_info->vsi_map); - } - - /* For non-VLAN rules that forward packets to a VSI list, convert them - * to forwarding packets to a VSI if there is only one VSI left in the - * list. Unused lists are then removed. - * VLAN rules need to use VSI lists even with only one VSI. - */ - if (fm_list_itr->fltr_info.fltr_act == ICE_FWD_TO_VSI_LIST) { - if (lkup_type == ICE_SW_LKUP_VLAN) { - del_list = is_last_elem; - } else if (fm_list_itr->vsi_count == 1) { - conv_list = true; - del_list = true; - } - } - - if (del_list) { - /* Remove the VSI list since it is no longer used */ - struct ice_vsi_list_map_info *vsi_list_info = - fm_list_itr->vsi_list_info; - - status = ice_remove_vsi_list_rule(hw, vsi_list_id, lkup_type); - if (status) - return status; - - if (conv_list) { - u16 rem_vsi_id; - - rem_vsi_id = find_first_bit(vsi_list_info->vsi_map, - ICE_MAX_VSI); - - /* Error out when the expected last element is not in - * the VSI list map - */ - if (rem_vsi_id == ICE_MAX_VSI) - return ICE_ERR_OUT_OF_RANGE; - - /* Change the list entry action from VSI_LIST to VSI */ - fm_list_itr->fltr_info.fltr_act = ICE_FWD_TO_VSI; - fm_list_itr->fltr_info.fwd_id.vsi_id = rem_vsi_id; - } - - list_del(&vsi_list_info->list_entry); - devm_kfree(ice_hw_to_dev(hw), vsi_list_info); - fm_list_itr->vsi_list_info = NULL; - } - - if (conv_list) { - /* Convert the rule's forward action to forwarding packets to - * a VSI - */ - struct ice_aqc_sw_rules_elem *s_rule; - - s_rule = devm_kzalloc(ice_hw_to_dev(hw), - ICE_SW_RULE_RX_TX_ETH_HDR_SIZE, - GFP_KERNEL); - if (!s_rule) - return ICE_ERR_NO_MEMORY; - - ice_fill_sw_rule(hw, &fm_list_itr->fltr_info, s_rule, - ice_aqc_opc_update_sw_rules); - - s_rule->pdata.lkup_tx_rx.index = - cpu_to_le16(fm_list_itr->fltr_info.fltr_rule_id); - - status = ice_aq_sw_rules(hw, s_rule, - ICE_SW_RULE_RX_TX_ETH_HDR_SIZE, 1, - ice_aqc_opc_update_sw_rules, NULL); - devm_kfree(ice_hw_to_dev(hw), s_rule); - if (status) - return status; - } - - if (is_last_elem) { - /* Remove the lookup rule */ - struct ice_aqc_sw_rules_elem *s_rule; - - s_rule = devm_kzalloc(ice_hw_to_dev(hw), - ICE_SW_RULE_RX_TX_NO_HDR_SIZE, - GFP_KERNEL); - if (!s_rule) - return ICE_ERR_NO_MEMORY; - - ice_fill_sw_rule(hw, &fm_list_itr->fltr_info, s_rule, - ice_aqc_opc_remove_sw_rules); - - status = ice_aq_sw_rules(hw, s_rule, - ICE_SW_RULE_RX_TX_NO_HDR_SIZE, 1, - ice_aqc_opc_remove_sw_rules, NULL); - if (status) - return status; - - /* Remove a book keeping entry from the MAC address list */ - mutex_lock(&sw->mac_list_lock); - list_del(&fm_list_itr->list_entry); - mutex_unlock(&sw->mac_list_lock); - devm_kfree(ice_hw_to_dev(hw), fm_list_itr); - devm_kfree(ice_hw_to_dev(hw), s_rule); - } - return status; -} - -/** - * ice_remove_mac_entry - * @hw: pointer to the hardware structure - * @f_entry: structure containing MAC forwarding information - */ -static enum ice_status -ice_remove_mac_entry(struct ice_hw *hw, struct ice_fltr_list_entry *f_entry) -{ - struct ice_fltr_mgmt_list_entry *m_entry; - u16 vsi_id; - u8 *add; - - add = &f_entry->fltr_info.l_data.mac.mac_addr[0]; - - m_entry = ice_find_mac_entry(hw, add); - if (!m_entry) - return ICE_ERR_PARAM; - - vsi_id = f_entry->fltr_info.fwd_id.vsi_id; - return ice_handle_rem_vsi_list_mgmt(hw, vsi_id, m_entry); -} - -/** - * ice_remove_mac - remove a MAC address based filter rule - * @hw: pointer to the hardware structure - * @m_list: list of MAC addresses and forwarding information - * - * This function removes either a MAC filter rule or a specific VSI from a - * VSI list for a multicast MAC address. - * - * Returns ICE_ERR_DOES_NOT_EXIST if a given entry was not added by - * ice_add_mac. Caller should be aware that this call will only work if all - * the entries passed into m_list were added previously. It will not attempt to - * do a partial remove of entries that were found. - */ -enum ice_status -ice_remove_mac(struct ice_hw *hw, struct list_head *m_list) -{ - struct ice_aqc_sw_rules_elem *s_rule, *r_iter; - u8 s_rule_size = ICE_SW_RULE_RX_TX_NO_HDR_SIZE; - struct ice_switch_info *sw = hw->switch_info; - struct ice_fltr_mgmt_list_entry *m_entry; - struct ice_fltr_list_entry *m_list_itr; - u16 elem_sent, total_elem_left; - enum ice_status status = 0; - u16 num_unicast = 0; - - if (!m_list) - return ICE_ERR_PARAM; - - list_for_each_entry(m_list_itr, m_list, list_entry) { - u8 *addr = m_list_itr->fltr_info.l_data.mac.mac_addr; - - if (is_unicast_ether_addr(addr) && !hw->ucast_shared) - num_unicast++; - else if (is_multicast_ether_addr(addr) || - (is_unicast_ether_addr(addr) && hw->ucast_shared)) - ice_remove_mac_entry(hw, m_list_itr); - } - - /* Exit if no unicast addresses found. Multicast switch rules - * were added individually - */ - if (!num_unicast) - return 0; - - /* Allocate switch rule buffer for the bulk update for unicast */ - s_rule = devm_kcalloc(ice_hw_to_dev(hw), num_unicast, s_rule_size, - GFP_KERNEL); - if (!s_rule) - return ICE_ERR_NO_MEMORY; - - r_iter = s_rule; - list_for_each_entry(m_list_itr, m_list, list_entry) { - u8 *addr = m_list_itr->fltr_info.l_data.mac.mac_addr; - - if (is_unicast_ether_addr(addr)) { - m_entry = ice_find_mac_entry(hw, addr); - if (!m_entry) { - status = ICE_ERR_DOES_NOT_EXIST; - goto ice_re |