Commit 80d144c9 authored by Anirudh Venkataramanan's avatar Anirudh Venkataramanan Committed by Jeff Kirsher

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: default avatarAnirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: default avatarTony Brelinski <tonyx.brelinski@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 74118f7a
...@@ -443,6 +443,8 @@ struct ice_aqc_vsi_props { ...@@ -443,6 +443,8 @@ struct ice_aqc_vsi_props {
u8 reserved[24]; u8 reserved[24];
}; };
#define ICE_MAX_NUM_RECIPES 64
/* Add/Update/Remove/Get switch rules (indirect 0x02A0, 0x02A1, 0x02A2, 0x02A3) /* Add/Update/Remove/Get switch rules (indirect 0x02A0, 0x02A1, 0x02A2, 0x02A3)
*/ */
struct ice_aqc_sw_rules { struct ice_aqc_sw_rules {
......
...@@ -388,20 +388,7 @@ static enum ice_status ice_init_fltr_mgmt_struct(struct ice_hw *hw) ...@@ -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); INIT_LIST_HEAD(&sw->vsi_list_map_head);
mutex_init(&sw->mac_list_lock); ice_init_def_sw_recp(hw);
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);
return 0; return 0;
} }
...@@ -415,19 +402,28 @@ static void ice_cleanup_fltr_mgmt_struct(struct ice_hw *hw) ...@@ -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_switch_info *sw = hw->switch_info;
struct ice_vsi_list_map_info *v_pos_map; struct ice_vsi_list_map_info *v_pos_map;
struct ice_vsi_list_map_info *v_tmp_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_for_each_entry_safe(v_pos_map, v_tmp_map, &sw->vsi_list_map_head,
list_entry) { list_entry) {
list_del(&v_pos_map->list_entry); list_del(&v_pos_map->list_entry);
devm_kfree(ice_hw_to_dev(hw), v_pos_map); 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); devm_kfree(ice_hw_to_dev(hw), sw->recp_list);
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); devm_kfree(ice_hw_to_dev(hw), sw);
} }
......
...@@ -85,6 +85,35 @@ ice_aq_alloc_free_res(struct ice_hw *hw, u16 num_entries, ...@@ -85,6 +85,35 @@ ice_aq_alloc_free_res(struct ice_hw *hw, u16 num_entries,
return ice_aq_send_cmd(hw, &desc, buf, buf_size, cd); return ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
} }
/**
* 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 * ice_aq_get_sw_cfg - get switch configuration
* @hw: pointer to the hardware structure * @hw: pointer to the hardware structure
...@@ -818,10 +847,10 @@ static enum ice_status ...@@ -818,10 +847,10 @@ static enum ice_status
ice_create_pkt_fwd_rule(struct ice_hw *hw, ice_create_pkt_fwd_rule(struct ice_hw *hw,
struct ice_fltr_list_entry *f_entry) 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_fltr_mgmt_list_entry *fm_entry;
struct ice_aqc_sw_rules_elem *s_rule; struct ice_aqc_sw_rules_elem *s_rule;
enum ice_sw_lkup_type l_type; enum ice_sw_lkup_type l_type;
struct ice_sw_recipe *recp;
enum ice_status status; enum ice_status status;
s_rule = devm_kzalloc(ice_hw_to_dev(hw), s_rule = devm_kzalloc(ice_hw_to_dev(hw),
...@@ -862,31 +891,9 @@ ice_create_pkt_fwd_rule(struct ice_hw *hw, ...@@ -862,31 +891,9 @@ ice_create_pkt_fwd_rule(struct ice_hw *hw,
* calls remove filter AQ command * calls remove filter AQ command
*/ */
l_type = fm_entry->fltr_info.lkup_type; l_type = fm_entry->fltr_info.lkup_type;
if (l_type == ICE_SW_LKUP_MAC) { recp = &hw->switch_info->recp_list[l_type];
mutex_lock(&sw->mac_list_lock); list_add(&fm_entry->list_entry, &recp->filt_rules);
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;
}
ice_create_pkt_fwd_rule_exit: ice_create_pkt_fwd_rule_exit:
devm_kfree(ice_hw_to_dev(hw), s_rule); devm_kfree(ice_hw_to_dev(hw), s_rule);
return status; return status;
...@@ -895,19 +902,15 @@ ice_create_pkt_fwd_rule(struct ice_hw *hw, ...@@ -895,19 +902,15 @@ ice_create_pkt_fwd_rule(struct ice_hw *hw,
/** /**
* ice_update_pkt_fwd_rule * ice_update_pkt_fwd_rule
* @hw: pointer to the hardware structure * @hw: pointer to the hardware structure
* @rule_id: rule of previously created switch rule to update * @f_info: filter information for switch rule
* @vsi_list_id: VSI list id to be updated with
* @f_info: ice_fltr_info to pull other information for switch rule
* *
* Call AQ command to update a previously created switch rule with a * Call AQ command to update a previously created switch rule with a
* VSI list id * VSI list id
*/ */
static enum ice_status static enum ice_status
ice_update_pkt_fwd_rule(struct ice_hw *hw, u16 rule_id, u16 vsi_list_id, ice_update_pkt_fwd_rule(struct ice_hw *hw, struct ice_fltr_info *f_info)
struct ice_fltr_info f_info)
{ {
struct ice_aqc_sw_rules_elem *s_rule; struct ice_aqc_sw_rules_elem *s_rule;
struct ice_fltr_info tmp_fltr;
enum ice_status status; enum ice_status status;
s_rule = devm_kzalloc(ice_hw_to_dev(hw), 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, ...@@ -915,14 +918,9 @@ ice_update_pkt_fwd_rule(struct ice_hw *hw, u16 rule_id, u16 vsi_list_id,
if (!s_rule) if (!s_rule)
return ICE_ERR_NO_MEMORY; return ICE_ERR_NO_MEMORY;
tmp_fltr = f_info; ice_fill_sw_rule(hw, f_info, s_rule, ice_aqc_opc_update_sw_rules);
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);
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 */ /* 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, 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, ...@@ -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 * @hw: pointer to the hardware structure
* @m_entry: pointer to current filter management list entry * @m_entry: pointer to current filter management list entry
* @cur_fltr: filter information from the book keeping entry * @cur_fltr: filter information from the book keeping entry
...@@ -954,7 +952,7 @@ ice_update_pkt_fwd_rule(struct ice_hw *hw, u16 rule_id, u16 vsi_list_id, ...@@ -954,7 +952,7 @@ ice_update_pkt_fwd_rule(struct ice_hw *hw, u16 rule_id, u16 vsi_list_id,
* using the update switch rule command * using the update switch rule command
*/ */
static enum ice_status static enum ice_status
ice_handle_vsi_list_mgmt(struct ice_hw *hw, ice_add_update_vsi_list(struct ice_hw *hw,
struct ice_fltr_mgmt_list_entry *m_entry, struct ice_fltr_mgmt_list_entry *m_entry,
struct ice_fltr_info *cur_fltr, struct ice_fltr_info *cur_fltr,
struct ice_fltr_info *new_fltr) struct ice_fltr_info *new_fltr)
...@@ -977,8 +975,8 @@ ice_handle_vsi_list_mgmt(struct ice_hw *hw, ...@@ -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 * a part of a VSI list. So, create a VSI list with the old and
* new VSIs. * new VSIs.
*/ */
struct ice_fltr_info tmp_fltr;
u16 vsi_id_arr[2]; u16 vsi_id_arr[2];
u16 fltr_rule;
/* A rule already exists with the new VSI being added */ /* A rule already exists with the new VSI being added */
if (cur_fltr->fwd_id.vsi_id == new_fltr->fwd_id.vsi_id) 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, ...@@ -992,12 +990,14 @@ ice_handle_vsi_list_mgmt(struct ice_hw *hw,
if (status) if (status)
return 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 /* Update the previous switch rule of "MAC forward to VSI" to
* "MAC fwd to VSI list" * "MAC fwd to VSI list"
*/ */
status = ice_update_pkt_fwd_rule(hw, fltr_rule, vsi_list_id, status = ice_update_pkt_fwd_rule(hw, &tmp_fltr);
*new_fltr);
if (status) if (status)
return status; return status;
...@@ -1042,54 +1042,245 @@ ice_handle_vsi_list_mgmt(struct ice_hw *hw, ...@@ -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 * @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 * Helper function to search for a given rule entry
* Returns pointer to the entry if found. * Returns pointer to entry storing the rule if found
*/ */
static struct ice_fltr_mgmt_list_entry * 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; struct ice_switch_info *sw = hw->switch_info;
struct list_head *list_head;
mutex_lock(&sw->mac_list_lock);
list_for_each_entry(m_list_itr, &sw->mac_list_head, list_entry) { list_head = &sw->recp_list[recp_id].filt_rules;
u8 *buf = &m_list_itr->fltr_info.l_data.mac.mac_addr[0]; list_for_each_entry(list_itr, list_head, list_entry) {
if (!memcmp(&f_info->l_data, &list_itr->fltr_info.l_data,
if (ether_addr_equal(buf, mac_addr)) { sizeof(f_info->l_data)) &&
mac_ret = m_list_itr; f_info->flag == list_itr->fltr_info.flag) {
ret = list_itr;
break; break;
} }
} }
mutex_unlock(&sw->mac_list_lock); return ret;
return mac_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 * @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 * @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 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_info *new_fltr, *cur_fltr;
struct ice_fltr_mgmt_list_entry *m_entry; struct ice_fltr_mgmt_list_entry *m_entry;
struct mutex *rule_lock; /* Lock to protect filter rule list */
enum ice_status status = 0;
rule_lock = &sw->recp_list[recp_id].filt_rule_lock;
mutex_lock(rule_lock);
new_fltr = &f_entry->fltr_info; 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_mac_entry(hw, &new_fltr->l_data.mac.mac_addr[0]); m_entry = ice_find_rule_entry(hw, recp_id, new_fltr);
if (!m_entry) if (!m_entry) {
mutex_unlock(rule_lock);
return ice_create_pkt_fwd_rule(hw, f_entry); return ice_create_pkt_fwd_rule(hw, f_entry);
}
cur_fltr = &m_entry->fltr_info; 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);
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;
}
return ice_handle_vsi_list_mgmt(hw, m_entry, cur_fltr, new_fltr); 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) ...@@ -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_aqc_sw_rules_elem *s_rule, *r_iter;
struct ice_fltr_list_entry *m_list_itr; struct ice_fltr_list_entry *m_list_itr;
struct list_head *rule_head;
u16 elem_sent, total_elem_left; 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; enum ice_status status = 0;
u16 num_unicast = 0; u16 num_unicast = 0;
u16 s_rule_size; u16 s_rule_size;
...@@ -1116,48 +1310,62 @@ ice_add_mac(struct ice_hw *hw, struct list_head *m_list) ...@@ -1116,48 +1310,62 @@ ice_add_mac(struct ice_hw *hw, struct list_head *m_list)
if (!m_list || !hw) if (!m_list || !hw)
return ICE_ERR_PARAM; 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) { list_for_each_entry(m_list_itr, m_list, list_entry) {
u8 *add = &m_list_itr->fltr_info.l_data.mac.mac_addr[0]; 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) m_list_itr->fltr_info.flag = ICE_FLTR_TX;
return ICE_ERR_PARAM; if (m_list_itr->fltr_info.lkup_type != ICE_SW_LKUP_MAC ||
if (is_zero_ether_addr(add)) is_zero_ether_addr(add))
return ICE_ERR_PARAM; return ICE_ERR_PARAM;
if (is_unicast_ether_addr(add) && !hw->ucast_shared) { if (is_unicast_ether_addr(add) && !hw->ucast_shared) {
/* Don't overwrite the unicast address */ /* 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; return ICE_ERR_ALREADY_EXISTS;
}
mutex_unlock(rule_lock);
num_unicast++; num_unicast++;
} else if (is_multicast_ether_addr(add) || } else if (is_multicast_ether_addr(add) ||
(is_unicast_ether_addr(add) && hw->ucast_shared)) { (is_unicast_ether_addr(add) && hw->ucast_shared)) {
status = ice_add_shared_mac(hw, m_list_itr); m_list_itr->status =
if (status) { ice_add_rule_internal(hw, ICE_SW_LKUP_MAC,
m_list_itr->status = ICE_FLTR_STATUS_FW_FAIL; m_list_itr);
return status; if (m_list_itr->status)
} return m_list_itr->status;
m_list_itr->status = ICE_FLTR_STATUS_FW_SUCCESS;
} }
} }
mutex_lock(rule_lock);
/* Exit if no suitable entries were found for adding bulk switch rule */ /* Exit if no suitable entries were found for adding bulk switch rule */
if (!num_unicast) if (!num_unicast) {
return 0; 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 */ /* Allocate switch rule buffer for the bulk update for unicast */
s_rule_size = ICE_SW_RULE_RX_TX_ETH_HDR_SIZE; 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, s_rule = devm_kcalloc(ice_hw_to_dev(hw), num_unicast, s_rule_size,
GFP_KERNEL); GFP_KERNEL);
if (!s_rule) if (!s_rule) {
return ICE_ERR_NO_MEMORY; status = ICE_ERR_NO_MEMORY;
goto ice_add_mac_exit;
}
r_iter = s_rule; r_iter = s_rule;
list_for_each_entry(m_list_itr, m_list, list_entry) { list_for_each_entry(m_list_itr, m_list, list_entry) {
struct ice_fltr_info *f_info = &m_list_itr->fltr_info; 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)) { if (is_unicast_ether_addr(mac_addr)) {
ice_fill_sw_rule(hw, &m_list_itr->fltr_info, ice_fill_sw_rule(hw, &m_list_itr->fltr_info, r_iter,
r_iter, ice_aqc_opc_add_sw_rules); ice_aqc_opc_add_sw_rules);
r_iter = (struct ice_aqc_sw_rules_elem *) r_iter = (struct ice_aqc_sw_rules_elem *)
((u8 *)r_iter + s_rule_size); ((u8 *)r_iter + s_rule_size);
} }
...@@ -1185,11 +1393,10 @@ ice_add_mac(struct ice_hw *hw, struct list_head *m_list) ...@@ -1185,11 +1393,10 @@ ice_add_mac(struct ice_hw *hw, struct list_head *m_list)
r_iter = s_rule; r_iter = s_rule;
list_for_each_entry(m_list_itr, m_list, list_entry) { list_for_each_entry(m_list_itr, m_list, list_entry) {
struct ice_fltr_info *f_info = &m_list_itr->fltr_info; 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];
struct ice_switch_info *sw = hw->switch_info;
struct ice_fltr_mgmt_list_entry *fm_entry; 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 = f_info->fltr_rule_id =
le16_to_cpu(r_iter->pdata.lkup_tx_rx.index); le16_to_cpu(r_iter->pdata.lkup_tx_rx.index);
f_info->fltr_act = ICE_FWD_TO_VSI; f_info->fltr_act = ICE_FWD_TO_VSI;
...@@ -1205,45 +1412,20 @@ ice_add_mac(struct ice_hw *hw, struct list_head *m_list) ...@@ -1205,45 +1412,20 @@ ice_add_mac(struct ice_hw *hw, struct list_head *m_list)
/* The book keeping entries will get removed when /* The book keeping entries will get removed when
* base driver calls remove filter AQ command * 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 *) r_iter = (struct ice_aqc_sw_rules_elem *)
((u8 *)r_iter + s_rule_size); ((u8 *)r_iter + s_rule_size);
} }
} }
ice_add_mac_exit: ice_add_mac_exit:
mutex_unlock(rule_lock);
if (s_rule)
devm_kfree(ice_hw_to_dev(hw), s_rule); devm_kfree(ice_hw_to_dev(hw), s_rule);
return status; 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 * ice_add_vlan_internal - Add one VLAN based filter rule
* @hw: pointer to the hardware structure * @hw: pointer to the hardware structure
...@@ -1252,20 +1434,22 @@ ice_find_vlan_entry(struct ice_hw *hw, u16 vlan_id) ...@@ -1252,20 +1434,22 @@ ice_find_vlan_entry(struct ice_hw *hw, u16 vlan_id)
static enum ice_status static enum ice_status
ice_add_vlan_internal(struct ice_hw *hw, struct ice_fltr_list_entry *f_entry) 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_info *new_fltr, *cur_fltr;
struct ice_fltr_mgmt_list_entry *v_list_itr; 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; new_fltr = &f_entry->fltr_info;
/* VLAN id should only be 12 bits */ /* VLAN id should only be 12 bits */
if (new_fltr->l_data.vlan.vlan_id > ICE_MAX_VLAN_ID) if (new_fltr->l_data.vlan.vlan_id > ICE_MAX_VLAN_ID)
return ICE_ERR_PARAM; return ICE_ERR_PARAM;
vlan_id = new_fltr->l_data.vlan.vlan_id; rule_lock = &sw->recp_list[ICE_SW_LKUP_VLAN].filt_rule_lock;
v_list_itr = ice_find_vlan_entry(hw, vlan_id); mutex_lock(rule_lock);
v_list_itr = ice_find_rule_entry(hw, ICE_SW_LKUP_VLAN, new_fltr);
if (!v_list_itr) { if (!v_list_itr) {
u16 vsi_id = ICE_VSI_INVAL_ID; u16 vsi_id = ICE_VSI_INVAL_ID;
enum ice_status status;
u16 vsi_list_id = 0; u16 vsi_list_id = 0;
if (new_fltr->fltr_act == ICE_FWD_TO_VSI) { 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) ...@@ -1279,26 +1463,33 @@ ice_add_vlan_internal(struct ice_hw *hw, struct ice_fltr_list_entry *f_entry)
&vsi_list_id, &vsi_list_id,
lkup_type); lkup_type);
if (status) if (status)
return status; goto exit;
new_fltr->fltr_act = ICE_FWD_TO_VSI_LIST; new_fltr->fltr_act = ICE_FWD_TO_VSI_LIST;
new_fltr->fwd_id.vsi_list_id = vsi_list_id; new_fltr->fwd_id.vsi_list_id = vsi_list_id;
} }
status = ice_create_pkt_fwd_rule(hw, f_entry); status = ice_create_pkt_fwd_rule(hw, f_entry);
if (!status && vsi_id != ICE_VSI_INVAL_ID) { if (!status && vsi_id != ICE_VSI_INVAL_ID) {
v_list_itr = ice_find_vlan_entry(hw, vlan_id); v_list_itr = ice_find_rule_entry(hw, ICE_SW_LKUP_VLAN,
if (!v_list_itr) new_fltr);
return ICE_ERR_DOES_NOT_EXIST; if (!v_list_itr) {
status = ICE_ERR_DOES_NOT_EXIST;
goto exit;
}
v_list_itr->vsi_list_info = v_list_itr->vsi_list_info =
ice_create_vsi_list_map(hw, &vsi_id, 1, ice_create_vsi_list_map(hw, &vsi_id, 1,
vsi_list_id); vsi_list_id);
} }
return status; goto exit;
} }
cur_fltr = &v_list_itr->fltr_info; 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,360 +1506,59 @@ ice_add_vlan(struct ice_hw *hw, struct list_head *v_list) ...@@ -1315,360 +1506,59 @@ ice_add_vlan(struct ice_hw *hw, struct list_head *v_list)
return ICE_ERR_PARAM; return ICE_ERR_PARAM;
list_for_each_entry(v_list_itr, v_list, list_entry) { 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) if (v_list_itr->fltr_info.lkup_type != ICE_SW_LKUP_VLAN)
return ICE_ERR_PARAM; return ICE_ERR_PARAM;
v_list_itr->fltr_info.flag = ICE_FLTR_TX;
status = ice_add_vlan_internal(hw, v_list_itr); v_list_itr->status = ice_add_vlan_internal(hw, v_list_itr);
if (status) { if (v_list_itr->status)
v_list_itr->status = ICE_FLTR_STATUS_FW_FAIL; return v_list_itr->status;
return status;
}
v_list_itr->status = ICE_FLTR_STATUS_FW_SUCCESS;
} }
return 0; return 0;
} }
/** /**
* ice_remove_vsi_list_rule * ice_cfg_dflt_vsi - change state of VSI to set/clear default
* @hw: pointer to the hardware structure * @hw: pointer to the hardware structure
* @vsi_list_id: VSI list id generated as part of allocate resource * @vsi_id: number of VSI to set as default
* @lkup_type: switch rule filter lookup type * @set: true to add the above mentioned switch rule, false to remove it
* @direction: ICE_FLTR_RX or ICE_FLTR_TX
*
* add filter rule to set/unset given VSI as default VSI for the switch
* (represented by swid)
*/ */
static enum ice_status enum ice_status
ice_remove_vsi_list_rule(struct ice_hw *hw, u16 vsi_list_id, ice_cfg_dflt_vsi(struct ice_hw *hw, u16 vsi_id, bool set, u8 direction)
enum ice_sw_lkup_type lkup_type)
{ {
struct ice_aqc_sw_rules_elem *s_rule; struct ice_aqc_sw_rules_elem *s_rule;
struct ice_fltr_info f_info;
enum ice_adminq_opc opcode;
enum ice_status status; enum ice_status status;
u16 s_rule_size; u16 s_rule_size;
s_rule_size = (u16)ICE_SW_RULE_VSI_LIST_SIZE(0); s_rule_size = set ? ICE_SW_RULE_RX_TX_ETH_HDR_SIZE :
ICE_SW_RULE_RX_TX_NO_HDR_SIZE;
s_rule = devm_kzalloc(ice_hw_to_dev(hw), s_rule_size, GFP_KERNEL); s_rule = devm_kzalloc(ice_hw_to_dev(hw), s_rule_size, GFP_KERNEL);
if (!s_rule) if (!s_rule)
return ICE_ERR_NO_MEMORY; return ICE_ERR_NO_MEMORY;
s_rule->type = cpu_to_le16(ICE_AQC_SW_RULES_T_VSI_LIST_CLEAR); memset(&f_info, 0, sizeof(f_info));
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;
}
/** f_info.lkup_type = ICE_SW_LKUP_DFLT;
* ice_handle_rem_vsi_list_mgmt f_info.flag = direction;
* @hw: pointer to the hardware structure f_info.fltr_act = ICE_FWD_TO_VSI;
* @vsi_id: ID of the VSI to remove f_info.fwd_id.vsi_id = vsi_id;
* @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; if (f_info.flag & ICE_FLTR_RX) {
vsi_list_id = fm_list_itr->fltr_info.fwd_id.vsi_list_id; f_info.src = hw->port_info->lport;
if (!set)
if (fm_list_itr->vsi_count > 1) { f_info.fltr_rule_id =
status = ice_update_vsi_list_rule(hw, &vsi_id, 1, vsi_list_id, hw->port_info->dflt_rx_vsi_rule_id;
true, } else if (f_info.flag & ICE_FLTR_TX) {
ice_aqc_opc_update_sw_rules, f_info.src = vsi_id;
lkup_type); if (!set)
if (status) f_info.fltr_rule_id =
return status; hw->port_info->dflt_tx_vsi_rule_id;
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_remove_mac_exit;
}
ice_fill_sw_rule(hw, &m_entry->fltr_info,
r_iter, ice_aqc_opc_remove_sw_rules);
r_iter = (struct ice_aqc_sw_rules_elem *)
((u8 *)r_iter + s_rule_size);
}
}
/* Call AQ bulk switch rule update for all unicast addresses */
r_iter = s_rule;
/* Call AQ switch rule in AQ_MAX chunk */
for (total_elem_left = num_unicast; total_elem_left > 0;
total_elem_left -= elem_sent) {
struct ice_aqc_sw_rules_elem *entry = r_iter;
elem_sent = min(total_elem_left,
(u16)(ICE_AQ_MAX_BUF_LEN / s_rule_size));
status = ice_aq_sw_rules(hw, entry, elem_sent * s_rule_size,
elem_sent, ice_aqc_opc_remove_sw_rules,
NULL);
if (status)
break;
r_iter = (struct ice_aqc_sw_rules_elem *)
((u8 *)r_iter + s_rule_size);
}
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)
return ICE_ERR_OUT_OF_RANGE;
mutex_lock(&sw->mac_list_lock);
list_del(&m_entry->list_entry);
mutex_unlock(&sw->mac_list_lock);
devm_kfree(ice_hw_to_dev(hw), m_entry);
}
}
ice_remove_mac_exit:
devm_kfree(ice_hw_to_dev(hw), s_rule);
return status;
}
/**
* ice_cfg_dflt_vsi - add filter rule to set/unset given VSI as default
* VSI for the switch (represented by swid)
* @hw: pointer to the hardware structure
* @vsi_id: number of VSI to set as default
* @set: true to add the above mentioned switch rule, false to remove it
* @direction: ICE_FLTR_RX or ICE_FLTR_TX
*/
enum ice_status
ice_cfg_dflt_vsi(struct ice_hw *hw, u16 vsi_id, bool set, u8 direction)
{
struct ice_aqc_sw_rules_elem *s_rule;
struct ice_fltr_info f_info;
enum ice_adminq_opc opcode;
enum ice_status status;
u16 s_rule_size;
s_rule_size = set ? ICE_SW_RULE_RX_TX_ETH_HDR_SIZE :
ICE_SW_RULE_RX_TX_NO_HDR_SIZE;
s_rule = devm_kzalloc(ice_hw_to_dev(hw), s_rule_size, GFP_KERNEL);
if (!s_rule)
return ICE_ERR_NO_MEMORY;
memset(&f_info, 0, sizeof(f_info));
f_info.lkup_type = ICE_SW_LKUP_DFLT;
f_info.flag = direction;
f_info.fltr_act = ICE_FWD_TO_VSI;
f_info.fwd_id.vsi_id = vsi_id;
if (f_info.flag & ICE_FLTR_RX) {
f_info.src = hw->port_info->lport;
if (!set)
f_info.fltr_rule_id =
hw->port_info->dflt_rx_vsi_rule_id;
} else if (f_info.flag & ICE_FLTR_TX) {
f_info.src = vsi_id;
if (!set)
f_info.fltr_rule_id =
hw->port_info->dflt_tx_vsi_rule_id;
}
if (set) if (set)
opcode = ice_aqc_opc_add_sw_rules; opcode = ice_aqc_opc_add_sw_rules;
...@@ -1706,26 +1596,38 @@ ice_cfg_dflt_vsi(struct ice_hw *hw, u16 vsi_id, bool set, u8 direction) ...@@ -1706,26 +1596,38 @@ ice_cfg_dflt_vsi(struct ice_hw *hw, u16 vsi_id, bool set, u8 direction)
} }
/** /**
* ice_remove_vlan_internal - Remove one VLAN based filter rule * ice_remove_mac - remove a MAC address based filter rule
* @hw: pointer to the hardware structure * @hw: pointer to the hardware structure
* @f_entry: filter entry containing one VLAN information * @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.
*/ */
static enum ice_status enum ice_status
ice_remove_vlan_internal(struct ice_hw *hw, ice_remove_mac(struct ice_hw *hw, struct list_head *m_list)
struct ice_fltr_list_entry *f_entry)
{ {
struct ice_fltr_info *new_fltr; struct ice_fltr_list_entry *list_itr;
struct ice_fltr_mgmt_list_entry *v_list_elem;
u16 vsi_id;
new_fltr = &f_entry->fltr_info; if (!m_list)
v_list_elem = ice_find_vlan_entry(hw, new_fltr->l_data.vlan.vlan_id);
if (!v_list_elem)
return ICE_ERR_PARAM; return ICE_ERR_PARAM;
vsi_id = f_entry->fltr_info.fwd_id.vsi_id; list_for_each_entry(list_itr, m_list, list_entry) {
return ice_handle_rem_vsi_list_mgmt(hw, vsi_id, v_list_elem); enum ice_sw_lkup_type l_type = list_itr->fltr_info.lkup_type;
if (l_type != ICE_SW_LKUP_MAC)
return ICE_ERR_PARAM;
list_itr->status = ice_remove_rule_internal(hw,
ICE_SW_LKUP_MAC,
list_itr);
if (list_itr->status)
return list_itr->status;
}
return 0;
} }
/** /**
...@@ -1737,20 +1639,78 @@ enum ice_status ...@@ -1737,20 +1639,78 @@ enum ice_status
ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list) ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list)
{ {
struct ice_fltr_list_entry *v_list_itr; struct ice_fltr_list_entry *v_list_itr;
enum ice_status status = 0;
if (!v_list || !hw) if (!v_list || !hw)
return ICE_ERR_PARAM; return ICE_ERR_PARAM;
list_for_each_entry(v_list_itr, v_list, list_entry) { list_for_each_entry(v_list_itr, v_list, list_entry) {
status = ice_remove_vlan_internal(hw, v_list_itr); enum ice_sw_lkup_type l_type = v_list_itr->fltr_info.lkup_type;
if (status) {
v_list_itr->status = ICE_FLTR_STATUS_FW_FAIL; if (l_type != ICE_SW_LKUP_VLAN)
return status; return ICE_ERR_PARAM;
} v_list_itr->status = ice_remove_rule_internal(hw,
v_list_itr->status = ICE_FLTR_STATUS_FW_SUCCESS; ICE_SW_LKUP_VLAN,
v_list_itr);
if (v_list_itr->status)
return v_list_itr->status;
} }
return status; return 0;
}
/**
* ice_vsi_uses_fltr - Determine if given VSI uses specified filter
* @fm_entry: filter entry to inspect
* @vsi_id: ID of VSI to compare with filter info
*/
static bool
ice_vsi_uses_fltr(struct ice_fltr_mgmt_list_entry *fm_entry, u16 vsi_id)
{
return ((fm_entry->fltr_info.fltr_act == ICE_FWD_TO_VSI &&
fm_entry->fltr_info.fwd_id.vsi_id == vsi_id) ||
(fm_entry->fltr_info.fltr_act == ICE_FWD_TO_VSI_LIST &&
(test_bit(vsi_id, fm_entry->vsi_list_info->vsi_map))));
}
/**
* ice_add_entry_to_vsi_fltr_list - Add copy of fltr_list_entry to remove list
* @hw: pointer to the hardware structure
* @vsi_id: ID of VSI to remove filters from
* @vsi_list_head: pointer to the list to add entry to
* @fi: pointer to fltr_info of filter entry to copy & add
*
* Helper function, used when creating a list of filters to remove from
* a specific VSI. The entry added to vsi_list_head is a COPY of the
* original filter entry, with the exception of fltr_info.fltr_act and
* fltr_info.fwd_id fields. These are set such that later logic can
* extract which VSI to remove the fltr from, and pass on that information.
*/
static enum ice_status
ice_add_entry_to_vsi_fltr_list(struct ice_hw *hw, u16 vsi_id,
struct list_head *vsi_list_head,
struct ice_fltr_info *fi)
{
struct ice_fltr_list_entry *tmp;
/* this memory is freed up in the caller function
* once filters for this VSI are removed
*/
tmp = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*tmp), GFP_KERNEL);
if (!tmp)
return ICE_ERR_NO_MEMORY;
tmp->fltr_info = *fi;
/* Overwrite these fields to indicate which VSI to remove filter from,
* so find and remove logic can extract the information from the
* list entries. Note that original entries will still have proper
* values.
*/
tmp->fltr_info.fltr_act = ICE_FWD_TO_VSI;
tmp->fltr_info.fwd_id.vsi_id = vsi_id;
list_add(&tmp->list_entry, vsi_list_head);
return 0;
} }
/** /**
...@@ -1759,6 +1719,12 @@ ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list) ...@@ -1759,6 +1719,12 @@ ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list)
* @vsi_id: ID of VSI to remove filters from * @vsi_id: ID of VSI to remove filters from
* @lkup_list_head: pointer to the list that has certain lookup type filters * @lkup_list_head: pointer to the list that has certain lookup type filters
* @vsi_list_head: pointer to the list pertaining to VSI with vsi_id * @vsi_list_head: pointer to the list pertaining to VSI with vsi_id
*
* Locates all filters in lkup_list_head that are used by the given VSI,
* and adds COPIES of those entries to vsi_list_head (intended to be used
* to remove the listed filters).
* Note that this means all entries in vsi_list_head must be explicitly
* deallocated by the caller when done with list.
*/ */
static enum ice_status static enum ice_status
ice_add_to_vsi_fltr_list(struct ice_hw *hw, u16 vsi_id, ice_add_to_vsi_fltr_list(struct ice_hw *hw, u16 vsi_id,
...@@ -1766,46 +1732,25 @@ ice_add_to_vsi_fltr_list(struct ice_hw *hw, u16 vsi_id, ...@@ -1766,46 +1732,25 @@ ice_add_to_vsi_fltr_list(struct ice_hw *hw, u16 vsi_id,
struct list_head *vsi_list_head) struct list_head *vsi_list_head)
{ {
struct ice_fltr_mgmt_list_entry *fm_entry; struct ice_fltr_mgmt_list_entry *fm_entry;
enum ice_status status = 0;
/* check to make sure VSI id is valid and within boundary */ /* check to make sure VSI id is valid and within boundary */
if (vsi_id >= if (vsi_id >= ICE_MAX_VSI)
(sizeof(fm_entry->vsi_list_info->vsi_map) * BITS_PER_BYTE - 1))
return ICE_ERR_PARAM; return ICE_ERR_PARAM;
list_for_each_entry(fm_entry, lkup_list_head, list_entry) { list_for_each_entry(fm_entry, lkup_list_head, list_entry) {
struct ice_fltr_info *fi; struct ice_fltr_info *fi;
fi = &fm_entry->fltr_info; fi = &fm_entry->fltr_info;
if ((fi->fltr_act == ICE_FWD_TO_VSI && if (!ice_vsi_uses_fltr(fm_entry, vsi_id))
fi->fwd_id.vsi_id == vsi_id) || continue;
(fi->fltr_act == ICE_FWD_TO_VSI_LIST &&
(test_bit(vsi_id, fm_entry->vsi_list_info->vsi_map)))) {
struct ice_fltr_list_entry *tmp;
/* this memory is freed up in the caller function
* ice_remove_vsi_lkup_fltr() once filters for
* this VSI are removed
*/
tmp = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*tmp),
GFP_KERNEL);
if (!tmp)
return ICE_ERR_NO_MEMORY;
memcpy(&tmp->fltr_info, fi, sizeof(*fi));
/* Expected below fields to be set to ICE_FWD_TO_VSI and
* the particular VSI id since we are only removing this
* one VSI
*/
if (fi->fltr_act == ICE_FWD_TO_VSI_LIST) {
tmp->fltr_info.fltr_act = ICE_FWD_TO_VSI;
tmp->fltr_info.fwd_id.vsi_id = vsi_id;
}
list_add(&tmp->list_entry, vsi_list_head); status = ice_add_entry_to_vsi_fltr_list(hw, vsi_id,
} vsi_list_head, fi);
if (status)
return status;
} }
return 0; return status;
} }
/** /**
...@@ -1821,46 +1766,40 @@ ice_remove_vsi_lkup_fltr(struct ice_hw *hw, u16 vsi_id, ...@@ -1821,46 +1766,40 @@ ice_remove_vsi_lkup_fltr(struct ice_hw *hw, u16 vsi_id,
struct ice_switch_info *sw = hw->switch_info; struct ice_switch_info *sw = hw->switch_info;
struct ice_fltr_list_entry *fm_entry; struct ice_fltr_list_entry *fm_entry;
struct list_head remove_list_head; struct list_head remove_list_head;
struct list_head *rule_head;
struct ice_fltr_list_entry *tmp; struct ice_fltr_list_entry *tmp;
struct mutex *rule_lock; /* Lock to protect filter rule list */
enum ice_status status; enum ice_status status;
INIT_LIST_HEAD(&remove_list_head); INIT_LIST_HEAD(&remove_list_head);
rule_lock = &sw->recp_list[lkup].filt_rule_lock;
rule_head = &sw->recp_list[lkup].filt_rules;
mutex_lock(rule_lock);
status = ice_add_to_vsi_fltr_list(hw, vsi_id, rule_head,
&remove_list_head);
mutex_unlock(rule_lock);
if (status)
return;
switch (lkup) { switch (lkup) {
case ICE_SW_LKUP_MAC: case ICE_SW_LKUP_MAC:
mutex_lock(&sw->mac_list_lock);
status = ice_add_to_vsi_fltr_list(hw, vsi_id,
&sw->mac_list_head,
&remove_list_head);
mutex_unlock(&sw->mac_list_lock);
if (!status) {
ice_remove_mac(hw, &remove_list_head); ice_remove_mac(hw, &remove_list_head);
goto free_fltr_list;
}
break; break;
case ICE_SW_LKUP_VLAN: case ICE_SW_LKUP_VLAN:
mutex_lock(&sw->vlan_list_lock);
status = ice_add_to_vsi_fltr_list(hw, vsi_id,
&sw->vlan_list_head,
&remove_list_head);
mutex_unlock(&sw->vlan_list_lock);
if (!status) {
ice_remove_vlan(hw, &remove_list_head); ice_remove_vlan(hw, &remove_list_head);
goto free_fltr_list;
}
break; break;
case ICE_SW_LKUP_MAC_VLAN: case ICE_SW_LKUP_MAC_VLAN:
case ICE_SW_LKUP_ETHERTYPE: case ICE_SW_LKUP_ETHERTYPE:
case ICE_SW_LKUP_ETHERTYPE_MAC: case ICE_SW_LKUP_ETHERTYPE_MAC:
case ICE_SW_LKUP_PROMISC: case ICE_SW_LKUP_PROMISC:
case ICE_SW_LKUP_PROMISC_VLAN:
case ICE_SW_LKUP_DFLT: case ICE_SW_LKUP_DFLT:
ice_debug(hw, ICE_DBG_SW, case ICE_SW_LKUP_PROMISC_VLAN:
"Remove filters for this lookup type hasn't been implemented yet\n"); case ICE_SW_LKUP_LAST:
default:
ice_debug(hw, ICE_DBG_SW, "Unsupported lookup type %d\n", lkup);
break; break;
} }
return;
free_fltr_list:
list_for_each_entry_safe(fm_entry, tmp, &remove_list_head, list_entry) { list_for_each_entry_safe(fm_entry, tmp, &remove_list_head, list_entry) {
list_del(&fm_entry->list_entry); list_del(&fm_entry->list_entry);
devm_kfree(ice_hw_to_dev(hw), fm_entry); devm_kfree(ice_hw_to_dev(hw), fm_entry);
......
...@@ -39,6 +39,7 @@ enum ice_sw_lkup_type { ...@@ -39,6 +39,7 @@ enum ice_sw_lkup_type {
ICE_SW_LKUP_DFLT = 5, ICE_SW_LKUP_DFLT = 5,
ICE_SW_LKUP_ETHERTYPE_MAC = 8, ICE_SW_LKUP_ETHERTYPE_MAC = 8,
ICE_SW_LKUP_PROMISC_VLAN = 9, ICE_SW_LKUP_PROMISC_VLAN = 9,
ICE_SW_LKUP_LAST
}; };
struct ice_fltr_info { struct ice_fltr_info {
...@@ -98,6 +99,31 @@ struct ice_fltr_info { ...@@ -98,6 +99,31 @@ struct ice_fltr_info {
u8 lan_en; /* Indicate if packet can be forwarded to the uplink */ u8 lan_en; /* Indicate if packet can be forwarded to the uplink */
}; };
struct ice_sw_recipe {
struct list_head l_entry;
/* To protect modification of filt_rule list
* defined below
*/
struct mutex filt_rule_lock;
/* List of type ice_fltr_mgmt_list_entry */
struct list_head filt_rules;
/* linked list of type recipe_list_entry */
struct list_head rg_list;
/* linked list of type ice_sw_fv_list_entry*/
struct list_head fv_list;
struct ice_aqc_recipe_data_elem *r_buf;
u8 recp_count;
u8 root_rid;
u8 num_profs;
u8 *prof_ids;
/* recipe bitmap: what all recipes makes this recipe */
DECLARE_BITMAP(r_bitmap, ICE_MAX_NUM_RECIPES);
};
/* Bookkeeping structure to hold bitmap of VSIs corresponding to VSI list id */ /* Bookkeeping structure to hold bitmap of VSIs corresponding to VSI list id */
struct ice_vsi_list_map_info { struct ice_vsi_list_map_info {
struct list_head list_entry; struct list_head list_entry;
...@@ -105,15 +131,9 @@ struct ice_vsi_list_map_info { ...@@ -105,15 +131,9 @@ struct ice_vsi_list_map_info {
u16 vsi_list_id; u16 vsi_list_id;
}; };
enum ice_sw_fltr_status {
ICE_FLTR_STATUS_NEW = 0,
ICE_FLTR_STATUS_FW_SUCCESS,
ICE_FLTR_STATUS_FW_FAIL,
};
struct ice_fltr_list_entry { struct ice_fltr_list_entry {
struct list_head list_entry; struct list_head list_entry;
enum ice_sw_fltr_status status; enum ice_status status;
struct ice_fltr_info fltr_info; struct ice_fltr_info fltr_info;
}; };
...@@ -157,5 +177,6 @@ enum ice_status ice_add_vlan(struct ice_hw *hw, struct list_head *m_list); ...@@ -157,5 +177,6 @@ enum ice_status ice_add_vlan(struct ice_hw *hw, struct list_head *m_list);
enum ice_status ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list); enum ice_status ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list);
enum ice_status enum ice_status
ice_cfg_dflt_vsi(struct ice_hw *hw, u16 vsi_id, bool set, u8 direction); ice_cfg_dflt_vsi(struct ice_hw *hw, u16 vsi_id, bool set, u8 direction);
enum ice_status ice_init_def_sw_recp(struct ice_hw *hw);
#endif /* _ICE_SWITCH_H_ */ #endif /* _ICE_SWITCH_H_ */
...@@ -253,19 +253,8 @@ struct ice_port_info { ...@@ -253,19 +253,8 @@ struct ice_port_info {
}; };
struct ice_switch_info { struct ice_switch_info {
/* Switch VSI lists to MAC/VLAN translation */
struct mutex mac_list_lock; /* protect MAC list */
struct list_head mac_list_head;
struct mutex vlan_list_lock; /* protect VLAN list */
struct list_head vlan_list_head;
struct mutex eth_m_list_lock; /* protect ethtype list */
struct list_head eth_m_list_head;
struct mutex promisc_list_lock; /* protect promisc mode list */
struct list_head promisc_list_head;
struct mutex mac_vlan_list_lock; /* protect MAC-VLAN list */
struct list_head mac_vlan_list_head;
struct list_head vsi_list_map_head; struct list_head vsi_list_map_head;
struct ice_sw_recipe *recp_list;
}; };
/* Port hardware description */ /* Port hardware description */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment