diff options
Diffstat (limited to 'drivers/net/ethernet/intel/ice/ice_switch.c')
| -rw-r--r-- | drivers/net/ethernet/intel/ice/ice_switch.c | 1686 | 
1 files changed, 1066 insertions, 620 deletions
| diff --git a/drivers/net/ethernet/intel/ice/ice_switch.c b/drivers/net/ethernet/intel/ice/ice_switch.c index 6b7ec2ae5ad6..33403f39f1b3 100644 --- a/drivers/net/ethernet/intel/ice/ice_switch.c +++ b/drivers/net/ethernet/intel/ice/ice_switch.c @@ -86,6 +86,36 @@ ice_aq_alloc_free_res(struct ice_hw *hw, u16 num_entries,  }  /** + * ice_init_def_sw_recp - initialize the recipe book keeping tables + * @hw: pointer to the hw struct + * + * Allocate memory for the entire recipe table and initialize the structures/ + * entries corresponding to basic recipes. + */ +enum ice_status +ice_init_def_sw_recp(struct ice_hw *hw) +{ +	struct ice_sw_recipe *recps; +	u8 i; + +	recps = devm_kcalloc(ice_hw_to_dev(hw), ICE_MAX_NUM_RECIPES, +			     sizeof(struct ice_sw_recipe), GFP_KERNEL); +	if (!recps) +		return ICE_ERR_NO_MEMORY; + +	for (i = 0; i < ICE_SW_LKUP_LAST; i++) { +		recps[i].root_rid = i; +		INIT_LIST_HEAD(&recps[i].filt_rules); +		INIT_LIST_HEAD(&recps[i].filt_replay_rules); +		mutex_init(&recps[i].filt_rule_lock); +	} + +	hw->switch_info->recp_list = recps; + +	return 0; +} + +/**   * ice_aq_get_sw_cfg - get switch configuration   * @hw: pointer to the hardware structure   * @buf: pointer to the result buffer @@ -140,23 +170,24 @@ ice_aq_get_sw_cfg(struct ice_hw *hw, struct ice_aqc_get_sw_cfg_resp *buf,   *   * Add a VSI context to the hardware (0x0210)   */ -enum ice_status +static enum ice_status  ice_aq_add_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,  	       struct ice_sq_cd *cd)  {  	struct ice_aqc_add_update_free_vsi_resp *res;  	struct ice_aqc_add_get_update_free_vsi *cmd; -	enum ice_status status;  	struct ice_aq_desc desc; +	enum ice_status status;  	cmd = &desc.params.vsi_cmd; -	res = (struct ice_aqc_add_update_free_vsi_resp *)&desc.params.raw; +	res = &desc.params.add_update_free_vsi_res;  	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_add_vsi);  	if (!vsi_ctx->alloc_from_pool)  		cmd->vsi_num = cpu_to_le16(vsi_ctx->vsi_num |  					   ICE_AQ_VSI_IS_VALID); +	cmd->vf_id = vsi_ctx->vf_num;  	cmd->vsi_flags = cpu_to_le16(vsi_ctx->flags); @@ -175,6 +206,42 @@ ice_aq_add_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,  }  /** + * ice_aq_free_vsi + * @hw: pointer to the hw struct + * @vsi_ctx: pointer to a VSI context struct + * @keep_vsi_alloc: keep VSI allocation as part of this PF's resources + * @cd: pointer to command details structure or NULL + * + * Free VSI context info from hardware (0x0213) + */ +static enum ice_status +ice_aq_free_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx, +		bool keep_vsi_alloc, struct ice_sq_cd *cd) +{ +	struct ice_aqc_add_update_free_vsi_resp *resp; +	struct ice_aqc_add_get_update_free_vsi *cmd; +	struct ice_aq_desc desc; +	enum ice_status status; + +	cmd = &desc.params.vsi_cmd; +	resp = &desc.params.add_update_free_vsi_res; + +	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_free_vsi); + +	cmd->vsi_num = cpu_to_le16(vsi_ctx->vsi_num | ICE_AQ_VSI_IS_VALID); +	if (keep_vsi_alloc) +		cmd->cmd_flags = cpu_to_le16(ICE_AQ_VSI_KEEP_ALLOC); + +	status = ice_aq_send_cmd(hw, &desc, NULL, 0, cd); +	if (!status) { +		vsi_ctx->vsis_allocd = le16_to_cpu(resp->vsi_used); +		vsi_ctx->vsis_unallocated = le16_to_cpu(resp->vsi_free); +	} + +	return status; +} + +/**   * ice_aq_update_vsi   * @hw: pointer to the hw struct   * @vsi_ctx: pointer to a VSI context struct @@ -182,7 +249,7 @@ ice_aq_add_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,   *   * Update VSI context in the hardware (0x0211)   */ -enum ice_status +static enum ice_status  ice_aq_update_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,  		  struct ice_sq_cd *cd)  { @@ -192,7 +259,7 @@ ice_aq_update_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,  	enum ice_status status;  	cmd = &desc.params.vsi_cmd; -	resp = (struct ice_aqc_add_update_free_vsi_resp *)&desc.params.raw; +	resp = &desc.params.add_update_free_vsi_res;  	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_update_vsi); @@ -212,42 +279,162 @@ ice_aq_update_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,  }  /** - * ice_aq_free_vsi + * ice_is_vsi_valid - check whether the VSI is valid or not + * @hw: pointer to the hw struct + * @vsi_handle: VSI handle + * + * check whether the VSI is valid or not + */ +bool ice_is_vsi_valid(struct ice_hw *hw, u16 vsi_handle) +{ +	return vsi_handle < ICE_MAX_VSI && hw->vsi_ctx[vsi_handle]; +} + +/** + * ice_get_hw_vsi_num - return the hw VSI number + * @hw: pointer to the hw struct + * @vsi_handle: VSI handle + * + * return the hw VSI number + * Caution: call this function only if VSI is valid (ice_is_vsi_valid) + */ +u16 ice_get_hw_vsi_num(struct ice_hw *hw, u16 vsi_handle) +{ +	return hw->vsi_ctx[vsi_handle]->vsi_num; +} + +/** + * ice_get_vsi_ctx - return the VSI context entry for a given VSI handle + * @hw: pointer to the hw struct + * @vsi_handle: VSI handle + * + * return the VSI context entry for a given VSI handle + */ +struct ice_vsi_ctx *ice_get_vsi_ctx(struct ice_hw *hw, u16 vsi_handle) +{ +	return (vsi_handle >= ICE_MAX_VSI) ? NULL : hw->vsi_ctx[vsi_handle]; +} + +/** + * ice_save_vsi_ctx - save the VSI context for a given VSI handle + * @hw: pointer to the hw struct + * @vsi_handle: VSI handle + * @vsi: VSI context pointer + * + * save the VSI context entry for a given VSI handle + */ +static void ice_save_vsi_ctx(struct ice_hw *hw, u16 vsi_handle, +			     struct ice_vsi_ctx *vsi) +{ +	hw->vsi_ctx[vsi_handle] = vsi; +} + +/** + * ice_clear_vsi_ctx - clear the VSI context entry + * @hw: pointer to the hw struct + * @vsi_handle: VSI handle + * + * clear the VSI context entry + */ +static void ice_clear_vsi_ctx(struct ice_hw *hw, u16 vsi_handle) +{ +	struct ice_vsi_ctx *vsi; + +	vsi = ice_get_vsi_ctx(hw, vsi_handle); +	if (vsi) { +		devm_kfree(ice_hw_to_dev(hw), vsi); +		hw->vsi_ctx[vsi_handle] = NULL; +	} +} + +/** + * ice_add_vsi - add VSI context to the hardware and VSI handle list   * @hw: pointer to the hw struct + * @vsi_handle: unique VSI handle provided by drivers   * @vsi_ctx: pointer to a VSI context struct - * @keep_vsi_alloc: keep VSI allocation as part of this PF's resources   * @cd: pointer to command details structure or NULL   * - * Get VSI context info from hardware (0x0213) + * Add a VSI context to the hardware also add it into the VSI handle list. + * If this function gets called after reset for existing VSIs then update + * with the new HW VSI number in the corresponding VSI handle list entry.   */  enum ice_status -ice_aq_free_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx, -		bool keep_vsi_alloc, struct ice_sq_cd *cd) +ice_add_vsi(struct ice_hw *hw, u16 vsi_handle, struct ice_vsi_ctx *vsi_ctx, +	    struct ice_sq_cd *cd)  { -	struct ice_aqc_add_update_free_vsi_resp *resp; -	struct ice_aqc_add_get_update_free_vsi *cmd; -	struct ice_aq_desc desc; +	struct ice_vsi_ctx *tmp_vsi_ctx;  	enum ice_status status; -	cmd = &desc.params.vsi_cmd; -	resp = (struct ice_aqc_add_update_free_vsi_resp *)&desc.params.raw; - -	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_free_vsi); +	if (vsi_handle >= ICE_MAX_VSI) +		return ICE_ERR_PARAM; +	status = ice_aq_add_vsi(hw, vsi_ctx, cd); +	if (status) +		return status; +	tmp_vsi_ctx = ice_get_vsi_ctx(hw, vsi_handle); +	if (!tmp_vsi_ctx) { +		/* Create a new vsi context */ +		tmp_vsi_ctx = devm_kzalloc(ice_hw_to_dev(hw), +					   sizeof(*tmp_vsi_ctx), GFP_KERNEL); +		if (!tmp_vsi_ctx) { +			ice_aq_free_vsi(hw, vsi_ctx, false, cd); +			return ICE_ERR_NO_MEMORY; +		} +		*tmp_vsi_ctx = *vsi_ctx; +		ice_save_vsi_ctx(hw, vsi_handle, tmp_vsi_ctx); +	} else { +		/* update with new HW VSI num */ +		if (tmp_vsi_ctx->vsi_num != vsi_ctx->vsi_num) +			tmp_vsi_ctx->vsi_num = vsi_ctx->vsi_num; +	} -	cmd->vsi_num = cpu_to_le16(vsi_ctx->vsi_num | ICE_AQ_VSI_IS_VALID); -	if (keep_vsi_alloc) -		cmd->cmd_flags = cpu_to_le16(ICE_AQ_VSI_KEEP_ALLOC); +	return status; +} -	status = ice_aq_send_cmd(hw, &desc, NULL, 0, cd); -	if (!status) { -		vsi_ctx->vsis_allocd = le16_to_cpu(resp->vsi_used); -		vsi_ctx->vsis_unallocated = le16_to_cpu(resp->vsi_free); -	} +/** + * ice_free_vsi- free VSI context from hardware and VSI handle list + * @hw: pointer to the hw struct + * @vsi_handle: unique VSI handle + * @vsi_ctx: pointer to a VSI context struct + * @keep_vsi_alloc: keep VSI allocation as part of this PF's resources + * @cd: pointer to command details structure or NULL + * + * Free VSI context info from hardware as well as from VSI handle list + */ +enum ice_status +ice_free_vsi(struct ice_hw *hw, u16 vsi_handle, struct ice_vsi_ctx *vsi_ctx, +	     bool keep_vsi_alloc, struct ice_sq_cd *cd) +{ +	enum ice_status status; +	if (!ice_is_vsi_valid(hw, vsi_handle)) +		return ICE_ERR_PARAM; +	vsi_ctx->vsi_num = ice_get_hw_vsi_num(hw, vsi_handle); +	status = ice_aq_free_vsi(hw, vsi_ctx, keep_vsi_alloc, cd); +	if (!status) +		ice_clear_vsi_ctx(hw, vsi_handle);  	return status;  }  /** + * ice_update_vsi + * @hw: pointer to the hw struct + * @vsi_handle: unique VSI handle + * @vsi_ctx: pointer to a VSI context struct + * @cd: pointer to command details structure or NULL + * + * Update VSI context in the hardware + */ +enum ice_status +ice_update_vsi(struct ice_hw *hw, u16 vsi_handle, struct ice_vsi_ctx *vsi_ctx, +	       struct ice_sq_cd *cd) +{ +	if (!ice_is_vsi_valid(hw, vsi_handle)) +		return ICE_ERR_PARAM; +	vsi_ctx->vsi_num = ice_get_hw_vsi_num(hw, vsi_handle); +	return ice_aq_update_vsi(hw, vsi_ctx, cd); +} + +/**   * ice_aq_alloc_free_vsi_list   * @hw: pointer to the hw struct   * @vsi_list_id: VSI list id returned or used for lookup @@ -464,10 +651,12 @@ ice_fill_sw_rule(struct ice_hw *hw, struct ice_fltr_info *f_info,  		 struct ice_aqc_sw_rules_elem *s_rule, enum ice_adminq_opc opc)  {  	u16 vlan_id = ICE_MAX_VLAN_ID + 1; -	u8 eth_hdr[DUMMY_ETH_HDR_LEN];  	void *daddr = NULL; +	u16 eth_hdr_sz; +	u8 *eth_hdr;  	u32 act = 0;  	__be16 *off; +	u8 q_rgn;  	if (opc == ice_aqc_opc_remove_sw_rules) {  		s_rule->pdata.lkup_tx_rx.act = 0; @@ -477,13 +666,16 @@ ice_fill_sw_rule(struct ice_hw *hw, struct ice_fltr_info *f_info,  		return;  	} +	eth_hdr_sz = sizeof(dummy_eth_header); +	eth_hdr = s_rule->pdata.lkup_tx_rx.hdr; +  	/* initialize the ether header with a dummy header */ -	memcpy(eth_hdr, dummy_eth_header, sizeof(dummy_eth_header)); +	memcpy(eth_hdr, dummy_eth_header, eth_hdr_sz);  	ice_fill_sw_info(hw, f_info);  	switch (f_info->fltr_act) {  	case ICE_FWD_TO_VSI: -		act |= (f_info->fwd_id.vsi_id << ICE_SINGLE_ACT_VSI_ID_S) & +		act |= (f_info->fwd_id.hw_vsi_id << ICE_SINGLE_ACT_VSI_ID_S) &  			ICE_SINGLE_ACT_VSI_ID_M;  		if (f_info->lkup_type != ICE_SW_LKUP_VLAN)  			act |= ICE_SINGLE_ACT_VSI_FORWARDING | @@ -503,14 +695,19 @@ ice_fill_sw_rule(struct ice_hw *hw, struct ice_fltr_info *f_info,  		act |= (f_info->fwd_id.q_id << ICE_SINGLE_ACT_Q_INDEX_S) &  			ICE_SINGLE_ACT_Q_INDEX_M;  		break; +	case ICE_DROP_PACKET: +		act |= ICE_SINGLE_ACT_VSI_FORWARDING | ICE_SINGLE_ACT_DROP | +			ICE_SINGLE_ACT_VALID_BIT; +		break;  	case ICE_FWD_TO_QGRP: +		q_rgn = f_info->qgrp_size > 0 ? +			(u8)ilog2(f_info->qgrp_size) : 0;  		act |= ICE_SINGLE_ACT_TO_Q; -		act |= (f_info->qgrp_size << ICE_SINGLE_ACT_Q_REGION_S) & +		act |= (f_info->fwd_id.q_id << ICE_SINGLE_ACT_Q_INDEX_S) & +			ICE_SINGLE_ACT_Q_INDEX_M; +		act |= (q_rgn << ICE_SINGLE_ACT_Q_REGION_S) &  			ICE_SINGLE_ACT_Q_REGION_M;  		break; -	case ICE_DROP_PACKET: -		act |= ICE_SINGLE_ACT_VSI_FORWARDING | ICE_SINGLE_ACT_DROP; -		break;  	default:  		return;  	} @@ -536,7 +733,7 @@ ice_fill_sw_rule(struct ice_hw *hw, struct ice_fltr_info *f_info,  		daddr = f_info->l_data.ethertype_mac.mac_addr;  		/* fall-through */  	case ICE_SW_LKUP_ETHERTYPE: -		off = (__be16 *)ð_hdr[ICE_ETH_ETHTYPE_OFFSET]; +		off = (__be16 *)(eth_hdr + ICE_ETH_ETHTYPE_OFFSET);  		*off = cpu_to_be16(f_info->l_data.ethertype_mac.ethertype);  		break;  	case ICE_SW_LKUP_MAC_VLAN: @@ -563,18 +760,16 @@ ice_fill_sw_rule(struct ice_hw *hw, struct ice_fltr_info *f_info,  	s_rule->pdata.lkup_tx_rx.act = cpu_to_le32(act);  	if (daddr) -		ether_addr_copy(ð_hdr[ICE_ETH_DA_OFFSET], daddr); +		ether_addr_copy(eth_hdr + ICE_ETH_DA_OFFSET, daddr);  	if (!(vlan_id > ICE_MAX_VLAN_ID)) { -		off = (__be16 *)ð_hdr[ICE_ETH_VLAN_TCI_OFFSET]; +		off = (__be16 *)(eth_hdr + ICE_ETH_VLAN_TCI_OFFSET);  		*off = cpu_to_be16(vlan_id);  	}  	/* Create the switch rule with the final dummy Ethernet header */  	if (opc != ice_aqc_opc_update_sw_rules) -		s_rule->pdata.lkup_tx_rx.hdr_len = cpu_to_le16(sizeof(eth_hdr)); - -	memcpy(s_rule->pdata.lkup_tx_rx.hdr, eth_hdr, sizeof(eth_hdr)); +		s_rule->pdata.lkup_tx_rx.hdr_len = cpu_to_le16(eth_hdr_sz);  }  /** @@ -601,8 +796,8 @@ ice_add_marker_act(struct ice_hw *hw, struct ice_fltr_mgmt_list_entry *m_ent,  	enum ice_status status;  	u16 lg_act_size;  	u16 rules_size; -	u16 vsi_info;  	u32 act; +	u16 id;  	if (m_ent->fltr_info.lkup_type != ICE_SW_LKUP_MAC)  		return ICE_ERR_PARAM; @@ -628,12 +823,11 @@ ice_add_marker_act(struct ice_hw *hw, struct ice_fltr_mgmt_list_entry *m_ent,  	/* First action VSI forwarding or VSI list forwarding depending on how  	 * many VSIs  	 */ -	vsi_info = (m_ent->vsi_count > 1) ? -		m_ent->fltr_info.fwd_id.vsi_list_id : -		m_ent->fltr_info.fwd_id.vsi_id; +	id = (m_ent->vsi_count > 1) ? m_ent->fltr_info.fwd_id.vsi_list_id : +		m_ent->fltr_info.fwd_id.hw_vsi_id;  	act = ICE_LG_ACT_VSI_FORWARDING | ICE_LG_ACT_VALID_BIT; -	act |= (vsi_info << ICE_LG_ACT_VSI_LIST_ID_S) & +	act |= (id << ICE_LG_ACT_VSI_LIST_ID_S) &  		ICE_LG_ACT_VSI_LIST_ID_M;  	if (m_ent->vsi_count > 1)  		act |= ICE_LG_ACT_VSI_LIST; @@ -686,15 +880,15 @@ ice_add_marker_act(struct ice_hw *hw, struct ice_fltr_mgmt_list_entry *m_ent,  /**   * ice_create_vsi_list_map   * @hw: pointer to the hardware structure - * @vsi_array: array of VSIs to form a VSI list - * @num_vsi: num VSI in the array + * @vsi_handle_arr: array of VSI handles to set in the VSI mapping + * @num_vsi: number of VSI handles in the array   * @vsi_list_id: VSI list id generated as part of allocate resource   *   * Helper function to create a new entry of VSI list id to VSI mapping   * using the given VSI list id   */  static struct ice_vsi_list_map_info * -ice_create_vsi_list_map(struct ice_hw *hw, u16 *vsi_array, u16 num_vsi, +ice_create_vsi_list_map(struct ice_hw *hw, u16 *vsi_handle_arr, u16 num_vsi,  			u16 vsi_list_id)  {  	struct ice_switch_info *sw = hw->switch_info; @@ -706,9 +900,9 @@ ice_create_vsi_list_map(struct ice_hw *hw, u16 *vsi_array, u16 num_vsi,  		return NULL;  	v_map->vsi_list_id = vsi_list_id; - +	v_map->ref_cnt = 1;  	for (i = 0; i < num_vsi; i++) -		set_bit(vsi_array[i], v_map->vsi_map); +		set_bit(vsi_handle_arr[i], v_map->vsi_map);  	list_add(&v_map->list_entry, &sw->vsi_list_map_head);  	return v_map; @@ -717,8 +911,8 @@ ice_create_vsi_list_map(struct ice_hw *hw, u16 *vsi_array, u16 num_vsi,  /**   * ice_update_vsi_list_rule   * @hw: pointer to the hardware structure - * @vsi_array: array of VSIs to form a VSI list - * @num_vsi: num VSI in the array + * @vsi_handle_arr: array of VSI handles to form a VSI list + * @num_vsi: number of VSI handles in the array   * @vsi_list_id: VSI list id generated as part of allocate resource   * @remove: Boolean value to indicate if this is a remove action   * @opc: switch rules population command type - pass in the command opcode @@ -728,7 +922,7 @@ ice_create_vsi_list_map(struct ice_hw *hw, u16 *vsi_array, u16 num_vsi,   * using the given VSI list id   */  static enum ice_status -ice_update_vsi_list_rule(struct ice_hw *hw, u16 *vsi_array, u16 num_vsi, +ice_update_vsi_list_rule(struct ice_hw *hw, u16 *vsi_handle_arr, u16 num_vsi,  			 u16 vsi_list_id, bool remove, enum ice_adminq_opc opc,  			 enum ice_sw_lkup_type lkup_type)  { @@ -759,9 +953,15 @@ ice_update_vsi_list_rule(struct ice_hw *hw, u16 *vsi_array, u16 num_vsi,  	s_rule = devm_kzalloc(ice_hw_to_dev(hw), s_rule_size, GFP_KERNEL);  	if (!s_rule)  		return ICE_ERR_NO_MEMORY; - -	for (i = 0; i < num_vsi; i++) -		s_rule->pdata.vsi_list.vsi[i] = cpu_to_le16(vsi_array[i]); +	for (i = 0; i < num_vsi; i++) { +		if (!ice_is_vsi_valid(hw, vsi_handle_arr[i])) { +			status = ICE_ERR_PARAM; +			goto exit; +		} +		/* AQ call requires hw_vsi_id(s) */ +		s_rule->pdata.vsi_list.vsi[i] = +			cpu_to_le16(ice_get_hw_vsi_num(hw, vsi_handle_arr[i])); +	}  	s_rule->type = cpu_to_le16(type);  	s_rule->pdata.vsi_list.number_vsi = cpu_to_le16(num_vsi); @@ -769,6 +969,7 @@ ice_update_vsi_list_rule(struct ice_hw *hw, u16 *vsi_array, u16 num_vsi,  	status = ice_aq_sw_rules(hw, s_rule, s_rule_size, 1, opc, NULL); +exit:  	devm_kfree(ice_hw_to_dev(hw), s_rule);  	return status;  } @@ -776,21 +977,16 @@ ice_update_vsi_list_rule(struct ice_hw *hw, u16 *vsi_array, u16 num_vsi,  /**   * ice_create_vsi_list_rule - Creates and populates a VSI list rule   * @hw: pointer to the hw struct - * @vsi_array: array of VSIs to form a VSI list - * @num_vsi: number of VSIs in the array + * @vsi_handle_arr: array of VSI handles to form a VSI list + * @num_vsi: number of VSI handles in the array   * @vsi_list_id: stores the ID of the VSI list to be created   * @lkup_type: switch rule filter's lookup type   */  static enum ice_status -ice_create_vsi_list_rule(struct ice_hw *hw, u16 *vsi_array, u16 num_vsi, +ice_create_vsi_list_rule(struct ice_hw *hw, u16 *vsi_handle_arr, u16 num_vsi,  			 u16 *vsi_list_id, enum ice_sw_lkup_type lkup_type)  {  	enum ice_status status; -	int i; - -	for (i = 0; i < num_vsi; i++) -		if (vsi_array[i] >= ICE_MAX_VSI) -			return ICE_ERR_OUT_OF_RANGE;  	status = ice_aq_alloc_free_vsi_list(hw, vsi_list_id, lkup_type,  					    ice_aqc_opc_alloc_res); @@ -798,9 +994,9 @@ ice_create_vsi_list_rule(struct ice_hw *hw, u16 *vsi_array, u16 num_vsi,  		return status;  	/* Update the newly created VSI list to include the specified VSIs */ -	return ice_update_vsi_list_rule(hw, vsi_array, num_vsi, *vsi_list_id, -					false, ice_aqc_opc_add_sw_rules, -					lkup_type); +	return ice_update_vsi_list_rule(hw, vsi_handle_arr, num_vsi, +					*vsi_list_id, false, +					ice_aqc_opc_add_sw_rules, lkup_type);  }  /** @@ -816,10 +1012,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), @@ -860,31 +1056,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; @@ -893,19 +1067,15 @@ ice_create_pkt_fwd_rule_exit:  /**   * ice_update_pkt_fwd_rule   * @hw: pointer to the hardware structure - * @rule_id: rule of previously created switch rule to update - * @vsi_list_id: VSI list id to be updated with - * @f_info: ice_fltr_info to pull other information for switch rule + * @f_info: filter information for switch rule   *   * Call AQ command to update a previously created switch rule with a   * VSI list id   */  static enum ice_status -ice_update_pkt_fwd_rule(struct ice_hw *hw, u16 rule_id, u16 vsi_list_id, -			struct ice_fltr_info f_info) +ice_update_pkt_fwd_rule(struct ice_hw *hw, struct ice_fltr_info *f_info)  {  	struct ice_aqc_sw_rules_elem *s_rule; -	struct ice_fltr_info tmp_fltr;  	enum ice_status status;  	s_rule = devm_kzalloc(ice_hw_to_dev(hw), @@ -913,14 +1083,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, @@ -931,7 +1096,48 @@ ice_update_pkt_fwd_rule(struct ice_hw *hw, u16 rule_id, u16 vsi_list_id,  }  /** - * ice_handle_vsi_list_mgmt + * ice_update_sw_rule_bridge_mode + * @hw: pointer to the hw struct + * + * Updates unicast switch filter rules based on VEB/VEPA mode + */ +enum ice_status ice_update_sw_rule_bridge_mode(struct ice_hw *hw) +{ +	struct ice_switch_info *sw = hw->switch_info; +	struct ice_fltr_mgmt_list_entry *fm_entry; +	enum ice_status status = 0; +	struct list_head *rule_head; +	struct mutex *rule_lock; /* Lock to protect filter rule list */ + +	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); +	list_for_each_entry(fm_entry, rule_head, list_entry) { +		struct ice_fltr_info *fi = &fm_entry->fltr_info; +		u8 *addr = fi->l_data.mac.mac_addr; + +		/* Update unicast Tx rules to reflect the selected +		 * VEB/VEPA mode +		 */ +		if ((fi->flag & ICE_FLTR_TX) && is_unicast_ether_addr(addr) && +		    (fi->fltr_act == ICE_FWD_TO_VSI || +		     fi->fltr_act == ICE_FWD_TO_VSI_LIST || +		     fi->fltr_act == ICE_FWD_TO_Q || +		     fi->fltr_act == ICE_FWD_TO_QGRP)) { +			status = ice_update_pkt_fwd_rule(hw, fi); +			if (status) +				break; +		} +	} + +	mutex_unlock(rule_lock); + +	return status; +} + +/** + * 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 @@ -952,10 +1158,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; @@ -975,34 +1181,36 @@ 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.  		 */ -		u16 vsi_id_arr[2]; -		u16 fltr_rule; +		struct ice_fltr_info tmp_fltr; +		u16 vsi_handle_arr[2];  		/* A rule already exists with the new VSI being added */ -		if (cur_fltr->fwd_id.vsi_id == new_fltr->fwd_id.vsi_id) +		if (cur_fltr->fwd_id.hw_vsi_id == new_fltr->fwd_id.hw_vsi_id)  			return ICE_ERR_ALREADY_EXISTS; -		vsi_id_arr[0] = cur_fltr->fwd_id.vsi_id; -		vsi_id_arr[1] = new_fltr->fwd_id.vsi_id; -		status = ice_create_vsi_list_rule(hw, &vsi_id_arr[0], 2, +		vsi_handle_arr[0] = cur_fltr->vsi_handle; +		vsi_handle_arr[1] = new_fltr->vsi_handle; +		status = ice_create_vsi_list_rule(hw, &vsi_handle_arr[0], 2,  						  &vsi_list_id,  						  new_fltr->lkup_type);  		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;  		cur_fltr->fwd_id.vsi_list_id = vsi_list_id;  		cur_fltr->fltr_act = ICE_FWD_TO_VSI_LIST;  		m_entry->vsi_list_info = -			ice_create_vsi_list_map(hw, &vsi_id_arr[0], 2, +			ice_create_vsi_list_map(hw, &vsi_handle_arr[0], 2,  						vsi_list_id);  		/* If this entry was large action then the large action needs @@ -1014,11 +1222,11 @@ ice_handle_vsi_list_mgmt(struct ice_hw *hw,  					       m_entry->sw_marker_id,  					       m_entry->lg_act_idx);  	} else { -		u16 vsi_id = new_fltr->fwd_id.vsi_id; +		u16 vsi_handle = new_fltr->vsi_handle;  		enum ice_adminq_opc opcode;  		/* A rule already exists with the new VSI being added */ -		if (test_bit(vsi_id, m_entry->vsi_list_info->vsi_map)) +		if (test_bit(vsi_handle, m_entry->vsi_list_info->vsi_map))  			return 0;  		/* Update the previously created VSI list set with @@ -1027,12 +1235,12 @@ ice_handle_vsi_list_mgmt(struct ice_hw *hw,  		vsi_list_id = cur_fltr->fwd_id.vsi_list_id;  		opcode = ice_aqc_opc_update_sw_rules; -		status = ice_update_vsi_list_rule(hw, &vsi_id, 1, vsi_list_id, -						  false, opcode, +		status = ice_update_vsi_list_rule(hw, &vsi_handle, 1, +						  vsi_list_id, false, opcode,  						  new_fltr->lkup_type);  		/* update VSI list mapping info with new VSI id */  		if (!status) -			set_bit(vsi_id, m_entry->vsi_list_info->vsi_map); +			set_bit(vsi_handle, m_entry->vsi_list_info->vsi_map);  	}  	if (!status)  		m_entry->vsi_count++; @@ -1040,54 +1248,313 @@ 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_find_vsi_list_entry - Search VSI list map with VSI count 1 + * @hw: pointer to the hardware structure + * @recp_id: lookup type for which VSI lists needs to be searched + * @vsi_handle: VSI handle to be found in VSI list + * @vsi_list_id: VSI list id found containing vsi_handle + * + * Helper function to search a VSI list with single entry containing given VSI + * handle element. This can be extended further to search VSI list with more + * than 1 vsi_count. Returns pointer to VSI list entry if found. + */ +static struct ice_vsi_list_map_info * +ice_find_vsi_list_entry(struct ice_hw *hw, u8 recp_id, u16 vsi_handle, +			u16 *vsi_list_id) +{ +	struct ice_vsi_list_map_info *map_info = NULL; +	struct ice_switch_info *sw = hw->switch_info; +	struct ice_fltr_mgmt_list_entry *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 (list_itr->vsi_count == 1 && list_itr->vsi_list_info) { +			map_info = list_itr->vsi_list_info; +			if (test_bit(vsi_handle, map_info->vsi_map)) { +				*vsi_list_id = map_info->vsi_list_id; +				return map_info; +			} +		} +	} +	return NULL;  }  /** - * 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; +	if (!ice_is_vsi_valid(hw, f_entry->fltr_info.vsi_handle)) +		return ICE_ERR_PARAM; +	f_entry->fltr_info.fwd_id.hw_vsi_id = +		ice_get_hw_vsi_num(hw, f_entry->fltr_info.vsi_handle); + +	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.hw_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 ice_handle_vsi_list_mgmt(hw, m_entry, cur_fltr, new_fltr); +	return status; +} + +/** + * ice_remove_vsi_list_rule + * @hw: pointer to the hardware structure + * @vsi_list_id: VSI list id generated as part of allocate resource + * @lkup_type: switch rule filter lookup type + * + * The VSI list should be emptied before this function is called to remove the + * VSI list. + */ +static enum ice_status +ice_remove_vsi_list_rule(struct ice_hw *hw, u16 vsi_list_id, +			 enum ice_sw_lkup_type lkup_type) +{ +	struct ice_aqc_sw_rules_elem *s_rule; +	enum ice_status status; +	u16 s_rule_size; + +	s_rule_size = (u16)ICE_SW_RULE_VSI_LIST_SIZE(0); +	s_rule = devm_kzalloc(ice_hw_to_dev(hw), s_rule_size, GFP_KERNEL); +	if (!s_rule) +		return ICE_ERR_NO_MEMORY; + +	s_rule->type = cpu_to_le16(ICE_AQC_SW_RULES_T_VSI_LIST_CLEAR); +	s_rule->pdata.vsi_list.index = cpu_to_le16(vsi_list_id); + +	/* Free the vsi_list resource that we allocated. It is assumed that the +	 * list is empty at this point. +	 */ +	status = ice_aq_alloc_free_vsi_list(hw, &vsi_list_id, lkup_type, +					    ice_aqc_opc_free_res); + +	devm_kfree(ice_hw_to_dev(hw), s_rule); +	return status; +} + +/** + * ice_rem_update_vsi_list + * @hw: pointer to the hardware structure + * @vsi_handle: VSI handle 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_handle, +			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_handle, 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_handle, 1, vsi_list_id, true, +					  ice_aqc_opc_update_sw_rules, +					  lkup_type); +	if (status) +		return status; + +	fm_list->vsi_count--; +	clear_bit(vsi_handle, fm_list->vsi_list_info->vsi_map); + +	if (fm_list->vsi_count == 1 && lkup_type != ICE_SW_LKUP_VLAN) { +		struct ice_fltr_info tmp_fltr_info = fm_list->fltr_info; +		struct ice_vsi_list_map_info *vsi_list_info = +			fm_list->vsi_list_info; +		u16 rem_vsi_handle; + +		rem_vsi_handle = find_first_bit(vsi_list_info->vsi_map, +						ICE_MAX_VSI); +		if (!ice_is_vsi_valid(hw, rem_vsi_handle)) +			return ICE_ERR_OUT_OF_RANGE; + +		/* Make sure VSI list is empty before removing it below */ +		status = ice_update_vsi_list_rule(hw, &rem_vsi_handle, 1, +						  vsi_list_id, true, +						  ice_aqc_opc_update_sw_rules, +						  lkup_type); +		if (status) +			return status; + +		tmp_fltr_info.fltr_act = ICE_FWD_TO_VSI; +		tmp_fltr_info.fwd_id.hw_vsi_id = +			ice_get_hw_vsi_num(hw, rem_vsi_handle); +		tmp_fltr_info.vsi_handle = rem_vsi_handle; +		status = ice_update_pkt_fwd_rule(hw, &tmp_fltr_info); +		if (status) { +			ice_debug(hw, ICE_DBG_SW, +				  "Failed to update pkt fwd rule to FWD_TO_VSI on HW VSI %d, error %d\n", +				  tmp_fltr_info.fwd_id.hw_vsi_id, status); +			return status; +		} + +		fm_list->fltr_info = tmp_fltr_info; +	} + +	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; + +		/* Remove the VSI list since it is no longer used */ +		status = ice_remove_vsi_list_rule(hw, vsi_list_id, lkup_type); +		if (status) { +			ice_debug(hw, ICE_DBG_SW, +				  "Failed to remove VSI list %d, error %d\n", +				  vsi_list_id, status); +			return status; +		} + +		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_handle; + +	if (!ice_is_vsi_valid(hw, f_entry->fltr_info.vsi_handle)) +		return ICE_ERR_PARAM; +	f_entry->fltr_info.fwd_id.hw_vsi_id = +		ice_get_hw_vsi_num(hw, f_entry->fltr_info.vsi_handle); + +	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 if (!list_elem->vsi_list_info) { +		status = ICE_ERR_DOES_NOT_EXIST; +		goto exit; +	} else { +		if (list_elem->vsi_list_info->ref_cnt > 1) +			list_elem->vsi_list_info->ref_cnt--; +		vsi_handle = f_entry->fltr_info.vsi_handle; +		status = ice_rem_update_vsi_list(hw, vsi_handle, 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;  }  /** @@ -1106,7 +1573,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; @@ -1114,48 +1584,73 @@ 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]; +		u16 vsi_handle; +		u16 hw_vsi_id; -		if (m_list_itr->fltr_info.lkup_type != ICE_SW_LKUP_MAC) +		m_list_itr->fltr_info.flag = ICE_FLTR_TX; +		vsi_handle = m_list_itr->fltr_info.vsi_handle; +		if (!ice_is_vsi_valid(hw, vsi_handle)) +			return ICE_ERR_PARAM; +		hw_vsi_id = ice_get_hw_vsi_num(hw, vsi_handle); +		m_list_itr->fltr_info.fwd_id.hw_vsi_id = hw_vsi_id; +		/* update the src in case it is vsi num */ +		if (m_list_itr->fltr_info.src_id != ICE_SRC_ID_VSI)  			return ICE_ERR_PARAM; -		if (is_zero_ether_addr(add)) +		m_list_itr->fltr_info.src = hw_vsi_id; +		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);  		} @@ -1183,11 +1678,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; @@ -1203,46 +1697,21 @@ ice_add_mac(struct ice_hw *hw, struct list_head *m_list)  			/* The book keeping entries will get removed when  			 * base driver calls remove filter AQ command  			 */ -			mutex_lock(&sw->mac_list_lock); -			list_add(&fm_entry->list_entry, &sw->mac_list_head); -			mutex_unlock(&sw->mac_list_lock); +			list_add(&fm_entry->list_entry, rule_head);  			r_iter = (struct ice_aqc_sw_rules_elem *)  				((u8 *)r_iter + s_rule_size);  		}  	}  ice_add_mac_exit: -	devm_kfree(ice_hw_to_dev(hw), s_rule); +	mutex_unlock(rule_lock); +	if (s_rule) +		devm_kfree(ice_hw_to_dev(hw), s_rule);  	return status;  }  /** - * ice_find_vlan_entry - * @hw: pointer to the hardware structure - * @vlan_id: VLAN id to search for - * - * Helper function to search for a VLAN entry using a given VLAN id - * Returns pointer to the entry if found. - */ -static struct ice_fltr_mgmt_list_entry * -ice_find_vlan_entry(struct ice_hw *hw, u16 vlan_id) -{ -	struct ice_fltr_mgmt_list_entry *vlan_list_itr, *vlan_ret = NULL; -	struct ice_switch_info *sw = hw->switch_info; - -	mutex_lock(&sw->vlan_list_lock); -	list_for_each_entry(vlan_list_itr, &sw->vlan_list_head, list_entry) -		if (vlan_list_itr->fltr_info.l_data.vlan.vlan_id == vlan_id) { -			vlan_ret = vlan_list_itr; -			break; -		} - -	mutex_unlock(&sw->vlan_list_lock); -	return vlan_ret; -} - -/**   * ice_add_vlan_internal - Add one VLAN based filter rule   * @hw: pointer to the hardware structure   * @f_entry: filter entry containing one VLAN information @@ -1250,53 +1719,150 @@ 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_fltr_info *new_fltr, *cur_fltr; +	struct ice_switch_info *sw = hw->switch_info;  	struct ice_fltr_mgmt_list_entry *v_list_itr; -	u16 vlan_id; +	struct ice_fltr_info *new_fltr, *cur_fltr; +	enum ice_sw_lkup_type lkup_type; +	u16 vsi_list_id = 0, vsi_handle; +	struct mutex *rule_lock; /* Lock to protect filter rule list */ +	enum ice_status status = 0; + +	if (!ice_is_vsi_valid(hw, f_entry->fltr_info.vsi_handle)) +		return ICE_ERR_PARAM; +	f_entry->fltr_info.fwd_id.hw_vsi_id = +		ice_get_hw_vsi_num(hw, f_entry->fltr_info.vsi_handle);  	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); +	if (new_fltr->src_id != ICE_SRC_ID_VSI) +		return ICE_ERR_PARAM; + +	new_fltr->src = new_fltr->fwd_id.hw_vsi_id; +	lkup_type = new_fltr->lkup_type; +	vsi_handle = new_fltr->vsi_handle; +	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; +		struct ice_vsi_list_map_info *map_info = NULL;  		if (new_fltr->fltr_act == ICE_FWD_TO_VSI) { -			enum ice_sw_lkup_type lkup_type = new_fltr->lkup_type; - -			/* All VLAN pruning rules use a VSI list. -			 * Convert the action to forwarding to a VSI list. +			/* All VLAN pruning rules use a VSI list. Check if +			 * there is already a VSI list containing VSI that we +			 * want to add. If found, use the same vsi_list_id for +			 * this new VLAN rule or else create a new list.  			 */ -			vsi_id = new_fltr->fwd_id.vsi_id; -			status = ice_create_vsi_list_rule(hw, &vsi_id, 1, -							  &vsi_list_id, -							  lkup_type); -			if (status) -				return status; +			map_info = ice_find_vsi_list_entry(hw, ICE_SW_LKUP_VLAN, +							   vsi_handle, +							   &vsi_list_id); +			if (!map_info) { +				status = ice_create_vsi_list_rule(hw, +								  &vsi_handle, +								  1, +								  &vsi_list_id, +								  lkup_type); +				if (status) +					goto exit; +			} +			/* Convert the action to forwarding to a VSI list. */  			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->vsi_list_info = -				ice_create_vsi_list_map(hw, &vsi_id, 1, -							vsi_list_id); +		if (!status) { +			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; +			} +			/* reuse VSI list for new rule and increment ref_cnt */ +			if (map_info) { +				v_list_itr->vsi_list_info = map_info; +				map_info->ref_cnt++; +			} else { +				v_list_itr->vsi_list_info = +					ice_create_vsi_list_map(hw, &vsi_handle, +								1, vsi_list_id); +			}  		} +	} else if (v_list_itr->vsi_list_info->ref_cnt == 1) { +		/* Update existing VSI list to add new VSI id only if it used +		 * by one VLAN rule. +		 */ +		cur_fltr = &v_list_itr->fltr_info; +		status = ice_add_update_vsi_list(hw, v_list_itr, cur_fltr, +						 new_fltr); +	} else { +		/* If VLAN rule exists and VSI list being used by this rule is +		 * referenced by more than 1 VLAN rule. Then create a new VSI +		 * list appending previous VSI with new VSI and update existing +		 * VLAN rule to point to new VSI list id +		 */ +		struct ice_fltr_info tmp_fltr; +		u16 vsi_handle_arr[2]; +		u16 cur_handle; -		return status; +		/* Current implementation only supports reusing VSI list with +		 * one VSI count. We should never hit below condition +		 */ +		if (v_list_itr->vsi_count > 1 && +		    v_list_itr->vsi_list_info->ref_cnt > 1) { +			ice_debug(hw, ICE_DBG_SW, +				  "Invalid configuration: Optimization to reuse VSI list with more than one VSI is not being done yet\n"); +			status = ICE_ERR_CFG; +			goto exit; +		} + +		cur_handle = +			find_first_bit(v_list_itr->vsi_list_info->vsi_map, +				       ICE_MAX_VSI); + +		/* A rule already exists with the new VSI being added */ +		if (cur_handle == vsi_handle) { +			status = ICE_ERR_ALREADY_EXISTS; +			goto exit; +		} + +		vsi_handle_arr[0] = cur_handle; +		vsi_handle_arr[1] = vsi_handle; +		status = ice_create_vsi_list_rule(hw, &vsi_handle_arr[0], 2, +						  &vsi_list_id, lkup_type); +		if (status) +			goto exit; + +		tmp_fltr = v_list_itr->fltr_info; +		tmp_fltr.fltr_rule_id = v_list_itr->fltr_info.fltr_rule_id; +		tmp_fltr.fwd_id.vsi_list_id = vsi_list_id; +		tmp_fltr.fltr_act = ICE_FWD_TO_VSI_LIST; +		/* Update the previous switch rule to a new VSI list which +		 * includes current VSI thats requested +		 */ +		status = ice_update_pkt_fwd_rule(hw, &tmp_fltr); +		if (status) +			goto exit; + +		/* before overriding VSI list map info. decrement ref_cnt of +		 * previous VSI list +		 */ +		v_list_itr->vsi_list_info->ref_cnt--; + +		/* now update to newly created list */ +		v_list_itr->fltr_info.fwd_id.vsi_list_id = vsi_list_id; +		v_list_itr->vsi_list_info = +			ice_create_vsi_list_map(hw, &vsi_handle_arr[0], 2, +						vsi_list_id); +		v_list_itr->vsi_count++;  	} -	cur_fltr = &v_list_itr->fltr_info; -	return ice_handle_vsi_list_mgmt(hw, v_list_itr, cur_fltr, new_fltr); +exit: +	mutex_unlock(rule_lock); +	return status;  }  /** @@ -1313,335 +1879,58 @@ 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 + * ice_rem_sw_rule_info   * @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 + * @rule_head: pointer to the switch list structure that we want to delete   */ -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) +static void +ice_rem_sw_rule_info(struct ice_hw *hw, struct list_head *rule_head)  { -	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; +	if (!list_empty(rule_head)) { +		struct ice_fltr_mgmt_list_entry *entry; +		struct ice_fltr_mgmt_list_entry *tmp; -		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_for_each_entry_safe(entry, tmp, rule_head, list_entry) { +			list_del(&entry->list_entry); +			devm_kfree(ice_hw_to_dev(hw), entry);  		} - -		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 + * ice_cfg_dflt_vsi - change state of VSI to set/clear default   * @hw: pointer to the hardware structure - * @m_list: list of MAC addresses and forwarding information - * - * This function removes either a MAC filter rule or a specific VSI from a - * VSI list for a multicast MAC address. - * - * Returns ICE_ERR_DOES_NOT_EXIST if a given entry was not added by - * ice_add_mac. Caller should be aware that this call will only work if all - * the entries passed into m_list were added previously. It will not attempt to - * do a partial remove of entries that were found. - */ -enum ice_status -ice_remove_mac(struct ice_hw *hw, struct list_head *m_list) -{ -	struct ice_aqc_sw_rules_elem *s_rule, *r_iter; -	u8 s_rule_size = ICE_SW_RULE_RX_TX_NO_HDR_SIZE; -	struct ice_switch_info *sw = hw->switch_info; -	struct ice_fltr_mgmt_list_entry *m_entry; -	struct ice_fltr_list_entry *m_list_itr; -	u16 elem_sent, total_elem_left; -	enum ice_status status = 0; -	u16 num_unicast = 0; - -	if (!m_list) -		return ICE_ERR_PARAM; - -	list_for_each_entry(m_list_itr, m_list, list_entry) { -		u8 *addr = m_list_itr->fltr_info.l_data.mac.mac_addr; - -		if (is_unicast_ether_addr(addr) && !hw->ucast_shared) -			num_unicast++; -		else if (is_multicast_ether_addr(addr) || -			 (is_unicast_ether_addr(addr) && hw->ucast_shared)) -			ice_remove_mac_entry(hw, m_list_itr); -	} - -	/* Exit if no unicast addresses found. Multicast switch rules -	 * were added individually -	 */ -	if (!num_unicast) -		return 0; - -	/* Allocate switch rule buffer for the bulk update for unicast */ -	s_rule = devm_kcalloc(ice_hw_to_dev(hw), num_unicast, s_rule_size, -			      GFP_KERNEL); -	if (!s_rule) -		return ICE_ERR_NO_MEMORY; - -	r_iter = s_rule; -	list_for_each_entry(m_list_itr, m_list, list_entry) { -		u8 *addr = m_list_itr->fltr_info.l_data.mac.mac_addr; - -		if (is_unicast_ether_addr(addr)) { -			m_entry = ice_find_mac_entry(hw, addr); -			if (!m_entry) { -				status = ICE_ERR_DOES_NOT_EXIST; -				goto ice_remove_mac_exit; -			} - -			ice_fill_sw_rule(hw, &m_entry->fltr_info, -					 r_iter, ice_aqc_opc_remove_sw_rules); -			r_iter = (struct ice_aqc_sw_rules_elem *) -				((u8 *)r_iter + s_rule_size); -		} -	} - -	/* Call AQ bulk switch rule update for all unicast addresses */ -	r_iter = s_rule; -	/* Call AQ switch rule in AQ_MAX chunk */ -	for (total_elem_left = num_unicast; total_elem_left > 0; -	     total_elem_left -= elem_sent) { -		struct ice_aqc_sw_rules_elem *entry = r_iter; - -		elem_sent = min(total_elem_left, -				(u16)(ICE_AQ_MAX_BUF_LEN / s_rule_size)); -		status = ice_aq_sw_rules(hw, entry, elem_sent * s_rule_size, -					 elem_sent, ice_aqc_opc_remove_sw_rules, -					 NULL); -		if (status) -			break; -		r_iter = (struct ice_aqc_sw_rules_elem *) -			((u8 *)r_iter + s_rule_size); -	} - -	list_for_each_entry(m_list_itr, m_list, list_entry) { -		u8 *addr = m_list_itr->fltr_info.l_data.mac.mac_addr; - -		if (is_unicast_ether_addr(addr)) { -			m_entry = ice_find_mac_entry(hw, addr); -			if (!m_entry) -				return ICE_ERR_OUT_OF_RANGE; -			mutex_lock(&sw->mac_list_lock); -			list_del(&m_entry->list_entry); -			mutex_unlock(&sw->mac_list_lock); -			devm_kfree(ice_hw_to_dev(hw), m_entry); -		} -	} - -ice_remove_mac_exit: -	devm_kfree(ice_hw_to_dev(hw), s_rule); -	return status; -} - -/** - * ice_cfg_dflt_vsi - add filter rule to set/unset given VSI as default - * VSI for the switch (represented by swid) - * @hw: pointer to the hardware structure - * @vsi_id: number of VSI to set as default + * @vsi_handle: VSI handle 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) +ice_cfg_dflt_vsi(struct ice_hw *hw, u16 vsi_handle, bool set, u8 direction)  {  	struct ice_aqc_sw_rules_elem *s_rule;  	struct ice_fltr_info f_info;  	enum ice_adminq_opc opcode;  	enum ice_status status;  	u16 s_rule_size; +	u16 hw_vsi_id; + +	if (!ice_is_vsi_valid(hw, vsi_handle)) +		return ICE_ERR_PARAM; +	hw_vsi_id = ice_get_hw_vsi_num(hw, vsi_handle);  	s_rule_size = set ? ICE_SW_RULE_RX_TX_ETH_HDR_SIZE :  			    ICE_SW_RULE_RX_TX_NO_HDR_SIZE; @@ -1654,15 +1943,17 @@ ice_cfg_dflt_vsi(struct ice_hw *hw, u16 vsi_id, bool set, u8 direction)  	f_info.lkup_type = ICE_SW_LKUP_DFLT;  	f_info.flag = direction;  	f_info.fltr_act = ICE_FWD_TO_VSI; -	f_info.fwd_id.vsi_id = vsi_id; +	f_info.fwd_id.hw_vsi_id = hw_vsi_id;  	if (f_info.flag & ICE_FLTR_RX) {  		f_info.src = hw->port_info->lport; +		f_info.src_id = ICE_SRC_ID_LPORT;  		if (!set)  			f_info.fltr_rule_id =  				hw->port_info->dflt_rx_vsi_rule_id;  	} else if (f_info.flag & ICE_FLTR_TX) { -		f_info.src = vsi_id; +		f_info.src_id = ICE_SRC_ID_VSI; +		f_info.src = hw_vsi_id;  		if (!set)  			f_info.fltr_rule_id =  				hw->port_info->dflt_tx_vsi_rule_id; @@ -1682,10 +1973,10 @@ ice_cfg_dflt_vsi(struct ice_hw *hw, u16 vsi_id, bool set, u8 direction)  		u16 index = le16_to_cpu(s_rule->pdata.lkup_tx_rx.index);  		if (f_info.flag & ICE_FLTR_TX) { -			hw->port_info->dflt_tx_vsi_num = vsi_id; +			hw->port_info->dflt_tx_vsi_num = hw_vsi_id;  			hw->port_info->dflt_tx_vsi_rule_id = index;  		} else if (f_info.flag & ICE_FLTR_RX) { -			hw->port_info->dflt_rx_vsi_num = vsi_id; +			hw->port_info->dflt_rx_vsi_num = hw_vsi_id;  			hw->port_info->dflt_rx_vsi_rule_id = index;  		}  	} else { @@ -1704,26 +1995,38 @@ out:  }  /** - * 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; +	struct ice_fltr_list_entry *list_itr, *tmp; -	new_fltr = &f_entry->fltr_info; - -	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_safe(list_itr, tmp, 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;  }  /** @@ -1734,131 +2037,169 @@ ice_remove_vlan_internal(struct ice_hw *hw,  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; +	struct ice_fltr_list_entry *v_list_itr, *tmp;  	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; +	list_for_each_entry_safe(v_list_itr, tmp, v_list, list_entry) { +		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_handle: VSI handle to compare with filter info + */ +static bool +ice_vsi_uses_fltr(struct ice_fltr_mgmt_list_entry *fm_entry, u16 vsi_handle) +{ +	return ((fm_entry->fltr_info.fltr_act == ICE_FWD_TO_VSI && +		 fm_entry->fltr_info.vsi_handle == vsi_handle) || +		(fm_entry->fltr_info.fltr_act == ICE_FWD_TO_VSI_LIST && +		 (test_bit(vsi_handle, 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_handle: VSI handle 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_handle, +			       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.vsi_handle = vsi_handle; +	tmp->fltr_info.fwd_id.hw_vsi_id = ice_get_hw_vsi_num(hw, vsi_handle); + +	list_add(&tmp->list_entry, vsi_list_head); + +	return 0;  }  /**   * ice_add_to_vsi_fltr_list - Add VSI filters to the list   * @hw: pointer to the hardware structure - * @vsi_id: ID of VSI to remove filters from + * @vsi_handle: VSI handle 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 + * @vsi_list_head: pointer to the list pertaining to VSI with vsi_handle + * + * 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, +ice_add_to_vsi_fltr_list(struct ice_hw *hw, u16 vsi_handle,  			 struct list_head *lkup_list_head,  			 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 (!ice_is_vsi_valid(hw, vsi_handle))  		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; +		if (!fi || !ice_vsi_uses_fltr(fm_entry, vsi_handle)) +			continue; -			memcpy(&tmp->fltr_info, fi, sizeof(*fi)); - -			/* Expected below fields to be set to ICE_FWD_TO_VSI and -			 * the particular VSI id since we are only removing this -			 * one VSI -			 */ -			if (fi->fltr_act == ICE_FWD_TO_VSI_LIST) { -				tmp->fltr_info.fltr_act = ICE_FWD_TO_VSI; -				tmp->fltr_info.fwd_id.vsi_id = vsi_id; -			} - -			list_add(&tmp->list_entry, vsi_list_head); -		} +		status = ice_add_entry_to_vsi_fltr_list(hw, vsi_handle, +							vsi_list_head, fi); +		if (status) +			return status;  	} -	return 0; +	return status;  }  /**   * ice_remove_vsi_lkup_fltr - Remove lookup type filters for a VSI   * @hw: pointer to the hardware structure - * @vsi_id: ID of VSI to remove filters from + * @vsi_handle: VSI handle to remove filters from   * @lkup: switch rule filter lookup type   */  static void -ice_remove_vsi_lkup_fltr(struct ice_hw *hw, u16 vsi_id, +ice_remove_vsi_lkup_fltr(struct ice_hw *hw, u16 vsi_handle,  			 enum ice_sw_lkup_type lkup)  {  	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[lkup].filt_rule_lock; +	rule_head = &sw->recp_list[lkup].filt_rules; +	mutex_lock(rule_lock); +	status = ice_add_to_vsi_fltr_list(hw, vsi_handle, 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); @@ -1868,16 +2209,121 @@ free_fltr_list:  /**   * ice_remove_vsi_fltr - Remove all filters for a VSI   * @hw: pointer to the hardware structure - * @vsi_id: ID of VSI to remove filters from + * @vsi_handle: VSI handle to remove filters from + */ +void ice_remove_vsi_fltr(struct ice_hw *hw, u16 vsi_handle) +{ +	ice_remove_vsi_lkup_fltr(hw, vsi_handle, ICE_SW_LKUP_MAC); +	ice_remove_vsi_lkup_fltr(hw, vsi_handle, ICE_SW_LKUP_MAC_VLAN); +	ice_remove_vsi_lkup_fltr(hw, vsi_handle, ICE_SW_LKUP_PROMISC); +	ice_remove_vsi_lkup_fltr(hw, vsi_handle, ICE_SW_LKUP_VLAN); +	ice_remove_vsi_lkup_fltr(hw, vsi_handle, ICE_SW_LKUP_DFLT); +	ice_remove_vsi_lkup_fltr(hw, vsi_handle, ICE_SW_LKUP_ETHERTYPE); +	ice_remove_vsi_lkup_fltr(hw, vsi_handle, ICE_SW_LKUP_ETHERTYPE_MAC); +	ice_remove_vsi_lkup_fltr(hw, vsi_handle, ICE_SW_LKUP_PROMISC_VLAN); +} + +/** + * ice_replay_vsi_fltr - Replay filters for requested VSI + * @hw: pointer to the hardware structure + * @vsi_handle: driver VSI handle + * @recp_id: Recipe id for which rules need to be replayed + * @list_head: list for which filters need to be replayed + * + * Replays the filter of recipe recp_id for a VSI represented via vsi_handle. + * It is required to pass valid VSI handle. + */ +static enum ice_status +ice_replay_vsi_fltr(struct ice_hw *hw, u16 vsi_handle, u8 recp_id, +		    struct list_head *list_head) +{ +	struct ice_fltr_mgmt_list_entry *itr; +	enum ice_status status = 0; +	u16 hw_vsi_id; + +	if (list_empty(list_head)) +		return status; +	hw_vsi_id = ice_get_hw_vsi_num(hw, vsi_handle); + +	list_for_each_entry(itr, list_head, list_entry) { +		struct ice_fltr_list_entry f_entry; + +		f_entry.fltr_info = itr->fltr_info; +		if (itr->vsi_count < 2 && recp_id != ICE_SW_LKUP_VLAN && +		    itr->fltr_info.vsi_handle == vsi_handle) { +			/* update the src in case it is vsi num */ +			if (f_entry.fltr_info.src_id == ICE_SRC_ID_VSI) +				f_entry.fltr_info.src = hw_vsi_id; +			status = ice_add_rule_internal(hw, recp_id, &f_entry); +			if (status) +				goto end; +			continue; +		} +		if (!itr->vsi_list_info || +		    !test_bit(vsi_handle, itr->vsi_list_info->vsi_map)) +			continue; +		/* Clearing it so that the logic can add it back */ +		clear_bit(vsi_handle, itr->vsi_list_info->vsi_map); +		f_entry.fltr_info.vsi_handle = vsi_handle; +		f_entry.fltr_info.fltr_act = ICE_FWD_TO_VSI; +		/* update the src in case it is vsi num */ +		if (f_entry.fltr_info.src_id == ICE_SRC_ID_VSI) +			f_entry.fltr_info.src = hw_vsi_id; +		if (recp_id == ICE_SW_LKUP_VLAN) +			status = ice_add_vlan_internal(hw, &f_entry); +		else +			status = ice_add_rule_internal(hw, recp_id, &f_entry); +		if (status) +			goto end; +	} +end: +	return status; +} + +/** + * ice_replay_vsi_all_fltr - replay all filters stored in bookkeeping lists + * @hw: pointer to the hardware structure + * @vsi_handle: driver VSI handle + * + * Replays filters for requested VSI via vsi_handle.   */ -void ice_remove_vsi_fltr(struct ice_hw *hw, u16 vsi_id) +enum ice_status ice_replay_vsi_all_fltr(struct ice_hw *hw, u16 vsi_handle)  { -	ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_MAC); -	ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_MAC_VLAN); -	ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_PROMISC); -	ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_VLAN); -	ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_DFLT); -	ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_ETHERTYPE); -	ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_ETHERTYPE_MAC); -	ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_PROMISC_VLAN); +	struct ice_switch_info *sw = hw->switch_info; +	enum ice_status status = 0; +	u8 i; + +	for (i = 0; i < ICE_SW_LKUP_LAST; i++) { +		struct list_head *head; + +		head = &sw->recp_list[i].filt_replay_rules; +		status = ice_replay_vsi_fltr(hw, vsi_handle, i, head); +		if (status) +			return status; +	} +	return status; +} + +/** + * ice_rm_all_sw_replay_rule_info - deletes filter replay rules + * @hw: pointer to the hw struct + * + * Deletes the filter replay rules. + */ +void ice_rm_all_sw_replay_rule_info(struct ice_hw *hw) +{ +	struct ice_switch_info *sw = hw->switch_info; +	u8 i; + +	if (!sw) +		return; + +	for (i = 0; i < ICE_SW_LKUP_LAST; i++) { +		if (!list_empty(&sw->recp_list[i].filt_replay_rules)) { +			struct list_head *l_head; + +			l_head = &sw->recp_list[i].filt_replay_rules; +			ice_rem_sw_rule_info(hw, l_head); +		} +	}  } |