diff mbox series

[v2,05/13] ice: Refactor switch rule management structures and functions

Message ID 20180801040433.5865-6-anirudh.venkataramanan@intel.com
State Superseded
Delegated to: Jeff Kirsher
Headers show
Series Feature updates for ice | expand

Commit Message

Anirudh Venkataramanan Aug. 1, 2018, 4:04 a.m. UTC
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>
---
 .../net/ethernet/intel/ice/ice_adminq_cmd.h   |   2 +
 drivers/net/ethernet/intel/ice/ice_common.c   |  36 +-
 drivers/net/ethernet/intel/ice/ice_switch.c   | 966 ++++++++----------
 drivers/net/ethernet/intel/ice/ice_switch.h   |  35 +-
 drivers/net/ethernet/intel/ice/ice_type.h     |  13 +-
 5 files changed, 499 insertions(+), 553 deletions(-)

Comments

Allan, Bruce W Aug. 1, 2018, 11:05 p.m. UTC | #1
> -----Original Message-----
> From: Intel-wired-lan [mailto:intel-wired-lan-bounces@osuosl.org] On Behalf
> Of Anirudh Venkataramanan
> Sent: Tuesday, July 31, 2018 9:04 PM
> To: intel-wired-lan@lists.osuosl.org
> Subject: [Intel-wired-lan] [PATCH v2 05/13] 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>
> ---
>  .../net/ethernet/intel/ice/ice_adminq_cmd.h   |   2 +
>  drivers/net/ethernet/intel/ice/ice_common.c   |  36 +-
>  drivers/net/ethernet/intel/ice/ice_switch.c   | 966 ++++++++----------
>  drivers/net/ethernet/intel/ice/ice_switch.h   |  35 +-
>  drivers/net/ethernet/intel/ice/ice_type.h     |  13 +-
>  5 files changed, 499 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 6d3e11659ba5..8bb97d6c632d 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 8f5c9fea7a36..a237aa53378a 100644
> --- a/drivers/net/ethernet/intel/ice/ice_common.c
> +++ b/drivers/net/ethernet/intel/ice/ice_common.c
> @@ -330,20 +330,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;
>  }
> @@ -357,19 +344,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..8b8241b51dd4 100644
> --- a/drivers/net/ethernet/intel/ice/ice_switch.c
> +++ b/drivers/net/ethernet/intel/ice/ice_switch.c
> @@ -85,6 +85,33 @@ ice_aq_alloc_free_res(struct ice_hw *hw, u16
> num_entries,
>  	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);

Earlier today I discovered this is missing a check for NULL returned by
devm_kcalloc() which would result in a NULL pointer dereference below.
I've already submitted a fix for this internally; this patch should be
updated to do the same.

> +
> +	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
> @@ -818,10 +845,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 +889,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 +900,15 @@ ice_create_pkt_fwd_rule(struct ice_hw *hw,
>  /**
>   * 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 +916,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, f_info, s_rule, ice_aqc_opc_update_sw_rules);
> 
> -	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 */
>  	status = ice_aq_sw_rules(hw, s_rule,
> ICE_SW_RULE_RX_TX_ETH_HDR_SIZE, 1,
> @@ -933,7 +929,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 +950,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 +973,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 +988,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 +1040,246 @@ 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 +1298,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 +1309,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 +1392,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,45 +1411,20 @@ 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
> @@ -1252,20 +1433,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 +1462,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 +1505,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_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)
> + * ice_cfg_dflt_vsi - change state of VSI to set/clear default
>   * @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
> + *
> + * add filter rule to set/unset given VSI as default VSI for the switch
> + * (represented by swid)
>   */
>  enum ice_status
>  ice_cfg_dflt_vsi(struct ice_hw *hw, u16 vsi_id, bool set, u8 direction)
> @@ -1706,26 +1595,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
> - * @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
> -ice_remove_vlan_internal(struct ice_hw *hw,
> -			 struct ice_fltr_list_entry *f_entry)
> +enum ice_status
> +ice_remove_mac(struct ice_hw *hw, struct list_head *m_list)
>  {
> -	struct ice_fltr_info *new_fltr;
> -	struct ice_fltr_mgmt_list_entry *v_list_elem;
> -	u16 vsi_id;
> -
> -	new_fltr = &f_entry->fltr_info;
> +	struct ice_fltr_list_entry *list_itr;
> 
> -	v_list_elem = ice_find_vlan_entry(hw, new_fltr->l_data.vlan.vlan_id);
> -	if (!v_list_elem)
> +	if (!m_list)
>  		return ICE_ERR_PARAM;
> 
> -	vsi_id = f_entry->fltr_info.fwd_id.vsi_id;
> -	return ice_handle_rem_vsi_list_mgmt(hw, vsi_id, v_list_elem);
> +	list_for_each_entry(list_itr, m_list, list_entry) {
> +		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 +1638,78 @@ enum ice_status
>  ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list)
>  {
>  	struct ice_fltr_list_entry *v_list_itr;
> -	enum ice_status status = 0;
> 
>  	if (!v_list || !hw)
>  		return ICE_ERR_PARAM;
> 
>  	list_for_each_entry(v_list_itr, v_list, list_entry) {
> -		status = ice_remove_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;
> +		enum ice_sw_lkup_type l_type = v_list_itr->fltr_info.lkup_type;
> +
> +		if (l_type != ICE_SW_LKUP_VLAN)
> +			return ICE_ERR_PARAM;
> +		v_list_itr->status = ice_remove_rule_internal(hw,
> +
> 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 +1718,12 @@ ice_remove_vlan(struct ice_hw *hw, struct
> list_head *v_list)
>   * @vsi_id: ID of VSI to remove filters from
>   * @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
> + *
> + * 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
>  ice_add_to_vsi_fltr_list(struct ice_hw *hw, u16 vsi_id,
> @@ -1766,46 +1731,25 @@ ice_add_to_vsi_fltr_list(struct ice_hw *hw, u16
> vsi_id,
>  			 struct list_head *vsi_list_head)
>  {
>  	struct ice_fltr_mgmt_list_entry *fm_entry;
> +	enum ice_status status = 0;
> 
>  	/* check to make sure VSI id is valid and within boundary */
> -	if (vsi_id >=
> -	    (sizeof(fm_entry->vsi_list_info->vsi_map) * BITS_PER_BYTE - 1))
> +	if (vsi_id >= ICE_MAX_VSI)
>  		return ICE_ERR_PARAM;
> 
>  	list_for_each_entry(fm_entry, lkup_list_head, list_entry) {
>  		struct ice_fltr_info *fi;
> 
>  		fi = &fm_entry->fltr_info;
> -		if ((fi->fltr_act == ICE_FWD_TO_VSI &&
> -		     fi->fwd_id.vsi_id == vsi_id) ||
> -		    (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;
> -			}
> +		if (!ice_vsi_uses_fltr(fm_entry, vsi_id))
> +			continue;
> 
> -			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 +1765,40 @@ ice_remove_vsi_lkup_fltr(struct ice_hw *hw, u16
> vsi_id,
>  	struct ice_switch_info *sw = hw->switch_info;
>  	struct ice_fltr_list_entry *fm_entry;
>  	struct list_head remove_list_head;
> +	struct list_head *rule_head;
>  	struct ice_fltr_list_entry *tmp;
> +	struct mutex *rule_lock;	/* Lock to protect filter rule list */
>  	enum ice_status status;
> 
>  	INIT_LIST_HEAD(&remove_list_head);
> +	rule_lock = &sw->recp_list[ICE_SW_LKUP_MAC].filt_rule_lock;
> +	rule_head = &sw->recp_list[ICE_SW_LKUP_MAC].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) {
>  	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);
> -			goto free_fltr_list;
> -		}
> +		ice_remove_mac(hw, &remove_list_head);
>  		break;
>  	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);
> -			goto free_fltr_list;
> -		}
> +		ice_remove_vlan(hw, &remove_list_head);
>  		break;
>  	case ICE_SW_LKUP_MAC_VLAN:
>  	case ICE_SW_LKUP_ETHERTYPE:
>  	case ICE_SW_LKUP_ETHERTYPE_MAC:
>  	case ICE_SW_LKUP_PROMISC:
> -	case ICE_SW_LKUP_PROMISC_VLAN:
>  	case ICE_SW_LKUP_DFLT:
> -		ice_debug(hw, ICE_DBG_SW,
> -			  "Remove filters for this lookup type hasn't been
> implemented yet\n");
> +	case ICE_SW_LKUP_PROMISC_VLAN:
> +	case ICE_SW_LKUP_LAST:
> +	default:
> +		ice_debug(hw, ICE_DBG_SW, "Unsupported lookup type
> %d\n", lkup);
>  		break;
>  	}
> 
> -	return;
> -free_fltr_list:
>  	list_for_each_entry_safe(fm_entry, tmp, &remove_list_head,
> list_entry) {
>  		list_del(&fm_entry->list_entry);
>  		devm_kfree(ice_hw_to_dev(hw), fm_entry);
> diff --git a/drivers/net/ethernet/intel/ice/ice_switch.h
> b/drivers/net/ethernet/intel/ice/ice_switch.h
> index 9b8ec128ee31..09a3b6d6541a 100644
> --- a/drivers/net/ethernet/intel/ice/ice_switch.h
> +++ b/drivers/net/ethernet/intel/ice/ice_switch.h
> @@ -39,6 +39,7 @@ enum ice_sw_lkup_type {
>  	ICE_SW_LKUP_DFLT = 5,
>  	ICE_SW_LKUP_ETHERTYPE_MAC = 8,
>  	ICE_SW_LKUP_PROMISC_VLAN = 9,
> +	ICE_SW_LKUP_LAST
>  };
> 
>  struct ice_fltr_info {
> @@ -98,6 +99,31 @@ struct ice_fltr_info {
>  	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 */
>  struct ice_vsi_list_map_info {
>  	struct list_head list_entry;
> @@ -105,15 +131,9 @@ struct ice_vsi_list_map_info {
>  	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 list_head list_entry;
> -	enum ice_sw_fltr_status status;
> +	enum ice_status status;
>  	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);
>  enum ice_status ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list);
>  enum ice_status
>  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_ */
> diff --git a/drivers/net/ethernet/intel/ice/ice_type.h
> b/drivers/net/ethernet/intel/ice/ice_type.h
> index bc31d1069cab..15458b034ed1 100644
> --- a/drivers/net/ethernet/intel/ice/ice_type.h
> +++ b/drivers/net/ethernet/intel/ice/ice_type.h
> @@ -248,19 +248,8 @@ struct ice_port_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 ice_sw_recipe *recp_list;
>  };
> 
>  /* Port hardware description */
> --
> 2.17.1
> 
> _______________________________________________
> Intel-wired-lan mailing list
> Intel-wired-lan@osuosl.org
> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan
Anirudh Venkataramanan Aug. 7, 2018, 5:04 p.m. UTC | #2
On Wed, 2018-08-01 at 16:05 -0700, Allan, Bruce W wrote:
> > -----Original Message-----
> > From: Intel-wired-lan [mailto:intel-wired-lan-bounces@osuosl.org]
> > On Behalf
> > Of Anirudh Venkataramanan
> > Sent: Tuesday, July 31, 2018 9:04 PM
> > To: intel-wired-lan@lists.osuosl.org
> > Subject: [Intel-wired-lan] [PATCH v2 05/13] 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>
> > ---
> >  .../net/ethernet/intel/ice/ice_adminq_cmd.h   |   2 +
> >  drivers/net/ethernet/intel/ice/ice_common.c   |  36 +-
> >  drivers/net/ethernet/intel/ice/ice_switch.c   | 966 ++++++++----
> > ------
> >  drivers/net/ethernet/intel/ice/ice_switch.h   |  35 +-
> >  drivers/net/ethernet/intel/ice/ice_type.h     |  13 +-
> >  5 files changed, 499 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 6d3e11659ba5..8bb97d6c632d 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 8f5c9fea7a36..a237aa53378a 100644
> > --- a/drivers/net/ethernet/intel/ice/ice_common.c
> > +++ b/drivers/net/ethernet/intel/ice/ice_common.c
> > @@ -330,20 +330,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;
> >  }
> > @@ -357,19 +344,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..8b8241b51dd4 100644
> > --- a/drivers/net/ethernet/intel/ice/ice_switch.c
> > +++ b/drivers/net/ethernet/intel/ice/ice_switch.c
> > @@ -85,6 +85,33 @@ ice_aq_alloc_free_res(struct ice_hw *hw, u16
> > num_entries,
> >  	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);
> 
> Earlier today I discovered this is missing a check for NULL returned
> by
> devm_kcalloc() which would result in a NULL pointer dereference
> below.
> I've already submitted a fix for this internally; this patch should
> be
> updated to do the same.

Yeah, already did. Will be part of the next revision.

> 
> > +
> > +	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
> > @@ -818,10 +845,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 +889,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 +900,15 @@ ice_create_pkt_fwd_rule(struct ice_hw *hw,
> >  /**
> >   * 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 +916,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, f_info, s_rule,
> > ice_aqc_opc_update_sw_rules);
> > 
> > -	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 */
> >  	status = ice_aq_sw_rules(hw, s_rule,
> > ICE_SW_RULE_RX_TX_ETH_HDR_SIZE, 1,
> > @@ -933,7 +929,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 +950,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 +973,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 +988,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 +1040,246 @@ 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 +1298,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 +1309,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 +1392,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,45 +1411,20 @@ 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
> > @@ -1252,20 +1433,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 +1462,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 +1505,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_re
> > s);
> > -
> > -	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_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)
> > + * ice_cfg_dflt_vsi - change state of VSI to set/clear default
> >   * @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
> > + *
> > + * add filter rule to set/unset given VSI as default VSI for the
> > switch
> > + * (represented by swid)
> >   */
> >  enum ice_status
> >  ice_cfg_dflt_vsi(struct ice_hw *hw, u16 vsi_id, bool set, u8
> > direction)
> > @@ -1706,26 +1595,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
> > - * @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
> > -ice_remove_vlan_internal(struct ice_hw *hw,
> > -			 struct ice_fltr_list_entry *f_entry)
> > +enum ice_status
> > +ice_remove_mac(struct ice_hw *hw, struct list_head *m_list)
> >  {
> > -	struct ice_fltr_info *new_fltr;
> > -	struct ice_fltr_mgmt_list_entry *v_list_elem;
> > -	u16 vsi_id;
> > -
> > -	new_fltr = &f_entry->fltr_info;
> > +	struct ice_fltr_list_entry *list_itr;
> > 
> > -	v_list_elem = ice_find_vlan_entry(hw, new_fltr-
> > >l_data.vlan.vlan_id);
> > -	if (!v_list_elem)
> > +	if (!m_list)
> >  		return ICE_ERR_PARAM;
> > 
> > -	vsi_id = f_entry->fltr_info.fwd_id.vsi_id;
> > -	return ice_handle_rem_vsi_list_mgmt(hw, vsi_id, v_list_elem);
> > +	list_for_each_entry(list_itr, m_list, list_entry) {
> > +		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 +1638,78 @@ enum ice_status
> >  ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list)
> >  {
> >  	struct ice_fltr_list_entry *v_list_itr;
> > -	enum ice_status status = 0;
> > 
> >  	if (!v_list || !hw)
> >  		return ICE_ERR_PARAM;
> > 
> >  	list_for_each_entry(v_list_itr, v_list, list_entry) {
> > -		status = ice_remove_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;
> > +		enum ice_sw_lkup_type l_type = v_list_itr-
> > >fltr_info.lkup_type;
> > +
> > +		if (l_type != ICE_SW_LKUP_VLAN)
> > +			return ICE_ERR_PARAM;
> > +		v_list_itr->status = ice_remove_rule_internal(hw,
> > +
> > ICE_SW_LKUP_VLAN,
> > +							      v_list_it
> > r);
> > +		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 +1718,12 @@ ice_remove_vlan(struct ice_hw *hw, struct
> > list_head *v_list)
> >   * @vsi_id: ID of VSI to remove filters from
> >   * @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
> > + *
> > + * 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
> >  ice_add_to_vsi_fltr_list(struct ice_hw *hw, u16 vsi_id,
> > @@ -1766,46 +1731,25 @@ ice_add_to_vsi_fltr_list(struct ice_hw *hw,
> > u16
> > vsi_id,
> >  			 struct list_head *vsi_list_head)
> >  {
> >  	struct ice_fltr_mgmt_list_entry *fm_entry;
> > +	enum ice_status status = 0;
> > 
> >  	/* check to make sure VSI id is valid and within boundary */
> > -	if (vsi_id >=
> > -	    (sizeof(fm_entry->vsi_list_info->vsi_map) * BITS_PER_BYTE -
> > 1))
> > +	if (vsi_id >= ICE_MAX_VSI)
> >  		return ICE_ERR_PARAM;
> > 
> >  	list_for_each_entry(fm_entry, lkup_list_head, list_entry) {
> >  		struct ice_fltr_info *fi;
> > 
> >  		fi = &fm_entry->fltr_info;
> > -		if ((fi->fltr_act == ICE_FWD_TO_VSI &&
> > -		     fi->fwd_id.vsi_id == vsi_id) ||
> > -		    (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;
> > -			}
> > +		if (!ice_vsi_uses_fltr(fm_entry, vsi_id))
> > +			continue;
> > 
> > -			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 +1765,40 @@ ice_remove_vsi_lkup_fltr(struct ice_hw *hw,
> > u16
> > vsi_id,
> >  	struct ice_switch_info *sw = hw->switch_info;
> >  	struct ice_fltr_list_entry *fm_entry;
> >  	struct list_head remove_list_head;
> > +	struct list_head *rule_head;
> >  	struct ice_fltr_list_entry *tmp;
> > +	struct mutex *rule_lock;	/* Lock to protect filter rule list
> > */
> >  	enum ice_status status;
> > 
> >  	INIT_LIST_HEAD(&remove_list_head);
> > +	rule_lock = &sw->recp_list[ICE_SW_LKUP_MAC].filt_rule_lock;
> > +	rule_head = &sw->recp_list[ICE_SW_LKUP_MAC].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) {
> >  	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);
> > -			goto free_fltr_list;
> > -		}
> > +		ice_remove_mac(hw, &remove_list_head);
> >  		break;
> >  	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);
> > -			goto free_fltr_list;
> > -		}
> > +		ice_remove_vlan(hw, &remove_list_head);
> >  		break;
> >  	case ICE_SW_LKUP_MAC_VLAN:
> >  	case ICE_SW_LKUP_ETHERTYPE:
> >  	case ICE_SW_LKUP_ETHERTYPE_MAC:
> >  	case ICE_SW_LKUP_PROMISC:
> > -	case ICE_SW_LKUP_PROMISC_VLAN:
> >  	case ICE_SW_LKUP_DFLT:
> > -		ice_debug(hw, ICE_DBG_SW,
> > -			  "Remove filters for this lookup type hasn't
> > been
> > implemented yet\n");
> > +	case ICE_SW_LKUP_PROMISC_VLAN:
> > +	case ICE_SW_LKUP_LAST:
> > +	default:
> > +		ice_debug(hw, ICE_DBG_SW, "Unsupported lookup type
> > %d\n", lkup);
> >  		break;
> >  	}
> > 
> > -	return;
> > -free_fltr_list:
> >  	list_for_each_entry_safe(fm_entry, tmp, &remove_list_head,
> > list_entry) {
> >  		list_del(&fm_entry->list_entry);
> >  		devm_kfree(ice_hw_to_dev(hw), fm_entry);
> > diff --git a/drivers/net/ethernet/intel/ice/ice_switch.h
> > b/drivers/net/ethernet/intel/ice/ice_switch.h
> > index 9b8ec128ee31..09a3b6d6541a 100644
> > --- a/drivers/net/ethernet/intel/ice/ice_switch.h
> > +++ b/drivers/net/ethernet/intel/ice/ice_switch.h
> > @@ -39,6 +39,7 @@ enum ice_sw_lkup_type {
> >  	ICE_SW_LKUP_DFLT = 5,
> >  	ICE_SW_LKUP_ETHERTYPE_MAC = 8,
> >  	ICE_SW_LKUP_PROMISC_VLAN = 9,
> > +	ICE_SW_LKUP_LAST
> >  };
> > 
> >  struct ice_fltr_info {
> > @@ -98,6 +99,31 @@ struct ice_fltr_info {
> >  	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 */
> >  struct ice_vsi_list_map_info {
> >  	struct list_head list_entry;
> > @@ -105,15 +131,9 @@ struct ice_vsi_list_map_info {
> >  	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 list_head list_entry;
> > -	enum ice_sw_fltr_status status;
> > +	enum ice_status status;
> >  	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);
> >  enum ice_status ice_remove_vlan(struct ice_hw *hw, struct
> > list_head *v_list);
> >  enum ice_status
> >  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_ */
> > diff --git a/drivers/net/ethernet/intel/ice/ice_type.h
> > b/drivers/net/ethernet/intel/ice/ice_type.h
> > index bc31d1069cab..15458b034ed1 100644
> > --- a/drivers/net/ethernet/intel/ice/ice_type.h
> > +++ b/drivers/net/ethernet/intel/ice/ice_type.h
> > @@ -248,19 +248,8 @@ struct ice_port_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 ice_sw_recipe *recp_list;
> >  };
> > 
> >  /* Port hardware description */
> > --
> > 2.17.1
> > 
> > _______________________________________________
> > Intel-wired-lan mailing list
> > Intel-wired-lan@osuosl.org
> > https://lists.osuosl.org/mailman/listinfo/intel-wired-lan
diff mbox series

Patch

diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index 6d3e11659ba5..8bb97d6c632d 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 8f5c9fea7a36..a237aa53378a 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -330,20 +330,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;
 }
@@ -357,19 +344,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..8b8241b51dd4 100644
--- a/drivers/net/ethernet/intel/ice/ice_switch.c
+++ b/drivers/net/ethernet/intel/ice/ice_switch.c
@@ -85,6 +85,33 @@  ice_aq_alloc_free_res(struct ice_hw *hw, u16 num_entries,
 	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);
+
+	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
@@ -818,10 +845,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 +889,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 +900,15 @@  ice_create_pkt_fwd_rule(struct ice_hw *hw,
 /**
  * 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 +916,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, f_info, s_rule, ice_aqc_opc_update_sw_rules);
 
-	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 */
 	status = ice_aq_sw_rules(hw, s_rule, ICE_SW_RULE_RX_TX_ETH_HDR_SIZE, 1,
@@ -933,7 +929,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 +950,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 +973,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 +988,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 +1040,246 @@  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 +1298,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 +1309,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 +1392,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,45 +1411,20 @@  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
@@ -1252,20 +1433,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 +1462,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 +1505,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_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)
+ * ice_cfg_dflt_vsi - change state of VSI to set/clear default
  * @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
+ *
+ * add filter rule to set/unset given VSI as default VSI for the switch
+ * (represented by swid)
  */
 enum ice_status
 ice_cfg_dflt_vsi(struct ice_hw *hw, u16 vsi_id, bool set, u8 direction)
@@ -1706,26 +1595,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
- * @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
-ice_remove_vlan_internal(struct ice_hw *hw,
-			 struct ice_fltr_list_entry *f_entry)
+enum ice_status
+ice_remove_mac(struct ice_hw *hw, struct list_head *m_list)
 {
-	struct ice_fltr_info *new_fltr;
-	struct ice_fltr_mgmt_list_entry *v_list_elem;
-	u16 vsi_id;
-
-	new_fltr = &f_entry->fltr_info;
+	struct ice_fltr_list_entry *list_itr;
 
-	v_list_elem = ice_find_vlan_entry(hw, new_fltr->l_data.vlan.vlan_id);
-	if (!v_list_elem)
+	if (!m_list)
 		return ICE_ERR_PARAM;
 
-	vsi_id = f_entry->fltr_info.fwd_id.vsi_id;
-	return ice_handle_rem_vsi_list_mgmt(hw, vsi_id, v_list_elem);
+	list_for_each_entry(list_itr, m_list, list_entry) {
+		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 +1638,78 @@  enum ice_status
 ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list)
 {
 	struct ice_fltr_list_entry *v_list_itr;
-	enum ice_status status = 0;
 
 	if (!v_list || !hw)
 		return ICE_ERR_PARAM;
 
 	list_for_each_entry(v_list_itr, v_list, list_entry) {
-		status = ice_remove_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;
+		enum ice_sw_lkup_type l_type = v_list_itr->fltr_info.lkup_type;
+
+		if (l_type != ICE_SW_LKUP_VLAN)
+			return ICE_ERR_PARAM;
+		v_list_itr->status = ice_remove_rule_internal(hw,
+							      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 +1718,12 @@  ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list)
  * @vsi_id: ID of VSI to remove filters from
  * @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
+ *
+ * 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
 ice_add_to_vsi_fltr_list(struct ice_hw *hw, u16 vsi_id,
@@ -1766,46 +1731,25 @@  ice_add_to_vsi_fltr_list(struct ice_hw *hw, u16 vsi_id,
 			 struct list_head *vsi_list_head)
 {
 	struct ice_fltr_mgmt_list_entry *fm_entry;
+	enum ice_status status = 0;
 
 	/* check to make sure VSI id is valid and within boundary */
-	if (vsi_id >=
-	    (sizeof(fm_entry->vsi_list_info->vsi_map) * BITS_PER_BYTE - 1))
+	if (vsi_id >= ICE_MAX_VSI)
 		return ICE_ERR_PARAM;
 
 	list_for_each_entry(fm_entry, lkup_list_head, list_entry) {
 		struct ice_fltr_info *fi;
 
 		fi = &fm_entry->fltr_info;
-		if ((fi->fltr_act == ICE_FWD_TO_VSI &&
-		     fi->fwd_id.vsi_id == vsi_id) ||
-		    (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;
-			}
+		if (!ice_vsi_uses_fltr(fm_entry, vsi_id))
+			continue;
 
-			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 +1765,40 @@  ice_remove_vsi_lkup_fltr(struct ice_hw *hw, u16 vsi_id,
 	struct ice_switch_info *sw = hw->switch_info;
 	struct ice_fltr_list_entry *fm_entry;
 	struct list_head remove_list_head;
+	struct list_head *rule_head;
 	struct ice_fltr_list_entry *tmp;
+	struct mutex *rule_lock;	/* Lock to protect filter rule list */
 	enum ice_status status;
 
 	INIT_LIST_HEAD(&remove_list_head);
+	rule_lock = &sw->recp_list[ICE_SW_LKUP_MAC].filt_rule_lock;
+	rule_head = &sw->recp_list[ICE_SW_LKUP_MAC].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) {
 	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);
-			goto free_fltr_list;
-		}
+		ice_remove_mac(hw, &remove_list_head);
 		break;
 	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);
-			goto free_fltr_list;
-		}
+		ice_remove_vlan(hw, &remove_list_head);
 		break;
 	case ICE_SW_LKUP_MAC_VLAN:
 	case ICE_SW_LKUP_ETHERTYPE:
 	case ICE_SW_LKUP_ETHERTYPE_MAC:
 	case ICE_SW_LKUP_PROMISC:
-	case ICE_SW_LKUP_PROMISC_VLAN:
 	case ICE_SW_LKUP_DFLT:
-		ice_debug(hw, ICE_DBG_SW,
-			  "Remove filters for this lookup type hasn't been implemented yet\n");
+	case ICE_SW_LKUP_PROMISC_VLAN:
+	case ICE_SW_LKUP_LAST:
+	default:
+		ice_debug(hw, ICE_DBG_SW, "Unsupported lookup type %d\n", lkup);
 		break;
 	}
 
-	return;
-free_fltr_list:
 	list_for_each_entry_safe(fm_entry, tmp, &remove_list_head, list_entry) {
 		list_del(&fm_entry->list_entry);
 		devm_kfree(ice_hw_to_dev(hw), fm_entry);
diff --git a/drivers/net/ethernet/intel/ice/ice_switch.h b/drivers/net/ethernet/intel/ice/ice_switch.h
index 9b8ec128ee31..09a3b6d6541a 100644
--- a/drivers/net/ethernet/intel/ice/ice_switch.h
+++ b/drivers/net/ethernet/intel/ice/ice_switch.h
@@ -39,6 +39,7 @@  enum ice_sw_lkup_type {
 	ICE_SW_LKUP_DFLT = 5,
 	ICE_SW_LKUP_ETHERTYPE_MAC = 8,
 	ICE_SW_LKUP_PROMISC_VLAN = 9,
+	ICE_SW_LKUP_LAST
 };
 
 struct ice_fltr_info {
@@ -98,6 +99,31 @@  struct ice_fltr_info {
 	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 */
 struct ice_vsi_list_map_info {
 	struct list_head list_entry;
@@ -105,15 +131,9 @@  struct ice_vsi_list_map_info {
 	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 list_head list_entry;
-	enum ice_sw_fltr_status status;
+	enum ice_status status;
 	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);
 enum ice_status ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list);
 enum ice_status
 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_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index bc31d1069cab..15458b034ed1 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -248,19 +248,8 @@  struct ice_port_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 ice_sw_recipe *recp_list;
 };
 
 /* Port hardware description */