Commit 95ad92d6 authored by Steven Zou's avatar Steven Zou Committed by Tony Nguyen
Browse files

ice: Add switch recipe reusing feature



New E810 firmware supports the corresponding functionality, so the driver
allows PFs to subscribe the same switch recipes. Then when the PF is done
with a switch recipes, the PF can ask firmware to free that switch recipe.

When users configure a rule to PFn into E810 switch component, if there is
no existing recipe matching this rule's pattern, the driver will request
firmware to allocate and return a new recipe resource for the rule by
calling ice_add_sw_recipe() and ice_alloc_recipe(). If there is an existing
recipe matching this rule's pattern with different key value, or this is a
same second rule to PFm into switch component, the driver checks out this
recipe by calling ice_find_recp(), the driver will tell firmware to share
using this same recipe resource by calling ice_subscribable_recp_shared()
and ice_subscribe_recipe().

When firmware detects that all subscribing PFs have freed the switch
recipe, firmware will free the switch recipe so that it can be reused.

This feature also fixes a problem where all switch recipes would eventually
be exhausted because switch recipes could not be freed, as freeing a shared
recipe could potentially break other PFs that were using it.

Reviewed-by: default avatarPrzemek Kitszel <przemyslaw.kitszel@intel.com>
Reviewed-by: default avatarAndrii Staikov <andrii.staikov@intel.com>
Reviewed-by: default avatarSimon Horman <horms@kernel.org>
Signed-off-by: default avatarSteven Zou <steven.zou@intel.com>
Tested-by: default avatarMayank Sharma <mayank.sharma@intel.com>
Signed-off-by: default avatarTony Nguyen <anthony.l.nguyen@intel.com>
parent 22118810
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -264,6 +264,8 @@ struct ice_aqc_set_port_params {
#define ICE_AQC_RES_TYPE_FLAG_SHARED			BIT(7)
#define ICE_AQC_RES_TYPE_FLAG_SCAN_BOTTOM		BIT(12)
#define ICE_AQC_RES_TYPE_FLAG_IGNORE_INDEX		BIT(13)
#define ICE_AQC_RES_TYPE_FLAG_SUBSCRIBE_SHARED		BIT(14)
#define ICE_AQC_RES_TYPE_FLAG_SUBSCRIBE_CTL		BIT(15)

#define ICE_AQC_RES_TYPE_FLAG_DEDICATED			0x00

+2 −0
Original line number Diff line number Diff line
@@ -1142,6 +1142,8 @@ int ice_init_hw(struct ice_hw *hw)
	if (status)
		goto err_unroll_fltr_mgmt_struct;
	mutex_init(&hw->tnl_lock);
	ice_init_chk_recipe_reuse_support(hw);

	return 0;

err_unroll_fltr_mgmt_struct:
+170 −17
Original line number Diff line number Diff line
@@ -2148,6 +2148,18 @@ ice_aq_get_recipe_to_profile(struct ice_hw *hw, u32 profile_id, u64 *r_assoc,
	return status;
}

/**
 * ice_init_chk_recipe_reuse_support - check if recipe reuse is supported
 * @hw: pointer to the hardware structure
 */
void ice_init_chk_recipe_reuse_support(struct ice_hw *hw)
{
	struct ice_nvm_info *nvm = &hw->flash.nvm;

	hw->recp_reuse = (nvm->major == 0x4 && nvm->minor >= 0x30) ||
			 nvm->major > 0x4;
}

/**
 * ice_alloc_recipe - add recipe resource
 * @hw: pointer to the hardware structure
@@ -2157,12 +2169,16 @@ int ice_alloc_recipe(struct ice_hw *hw, u16 *rid)
{
	DEFINE_RAW_FLEX(struct ice_aqc_alloc_free_res_elem, sw_buf, elem, 1);
	u16 buf_len = __struct_size(sw_buf);
	u16 res_type;
	int status;

	sw_buf->num_elems = cpu_to_le16(1);
	sw_buf->res_type = cpu_to_le16((ICE_AQC_RES_TYPE_RECIPE <<
					ICE_AQC_RES_TYPE_S) |
					ICE_AQC_RES_TYPE_FLAG_SHARED);
	res_type = FIELD_PREP(ICE_AQC_RES_TYPE_M, ICE_AQC_RES_TYPE_RECIPE);
	if (hw->recp_reuse)
		res_type |= ICE_AQC_RES_TYPE_FLAG_SUBSCRIBE_SHARED;
	else
		res_type |= ICE_AQC_RES_TYPE_FLAG_SHARED;
	sw_buf->res_type = cpu_to_le16(res_type);
	status = ice_aq_alloc_free_res(hw, sw_buf, buf_len,
				       ice_aqc_opc_alloc_res);
	if (!status)
@@ -2171,6 +2187,70 @@ int ice_alloc_recipe(struct ice_hw *hw, u16 *rid)
	return status;
}

/**
 * ice_free_recipe_res - free recipe resource
 * @hw: pointer to the hardware structure
 * @rid: recipe ID to free
 *
 * Return: 0 on success, and others on error
 */
static int ice_free_recipe_res(struct ice_hw *hw, u16 rid)
{
	return ice_free_hw_res(hw, ICE_AQC_RES_TYPE_RECIPE, 1, &rid);
}

/**
 * ice_release_recipe_res - disassociate and free recipe resource
 * @hw: pointer to the hardware structure
 * @recp: the recipe struct resource to unassociate and free
 *
 * Return: 0 on success, and others on error
 */
static int ice_release_recipe_res(struct ice_hw *hw,
				  struct ice_sw_recipe *recp)
{
	DECLARE_BITMAP(r_bitmap, ICE_MAX_NUM_RECIPES);
	struct ice_switch_info *sw = hw->switch_info;
	u64 recp_assoc;
	u32 rid, prof;
	int status;

	for_each_set_bit(rid, recp->r_bitmap, ICE_MAX_NUM_RECIPES) {
		for_each_set_bit(prof, recipe_to_profile[rid],
				 ICE_MAX_NUM_PROFILES) {
			status = ice_aq_get_recipe_to_profile(hw, prof,
							      &recp_assoc,
							      NULL);
			if (status)
				return status;

			bitmap_from_arr64(r_bitmap, &recp_assoc,
					  ICE_MAX_NUM_RECIPES);
			bitmap_andnot(r_bitmap, r_bitmap, recp->r_bitmap,
				      ICE_MAX_NUM_RECIPES);
			bitmap_to_arr64(&recp_assoc, r_bitmap,
					ICE_MAX_NUM_RECIPES);
			ice_aq_map_recipe_to_profile(hw, prof,
						     recp_assoc, NULL);

			clear_bit(rid, profile_to_recipe[prof]);
			clear_bit(prof, recipe_to_profile[rid]);
		}

		status = ice_free_recipe_res(hw, rid);
		if (status)
			return status;

		sw->recp_list[rid].recp_created = false;
		sw->recp_list[rid].adv_rule = false;
		memset(&sw->recp_list[rid].lkup_exts, 0,
		       sizeof(sw->recp_list[rid].lkup_exts));
		clear_bit(rid, recp->r_bitmap);
	}

	return 0;
}

/**
 * ice_get_recp_to_prof_map - updates recipe to profile mapping
 * @hw: pointer to hardware structure
@@ -2220,6 +2300,7 @@ ice_collect_result_idx(struct ice_aqc_recipe_data_elem *buf,
 * @recps: struct that we need to populate
 * @rid: recipe ID that we are populating
 * @refresh_required: true if we should get recipe to profile mapping from FW
 * @is_add: flag of adding recipe
 *
 * This function is used to populate all the necessary entries into our
 * bookkeeping so that we have a current list of all the recipes that are
@@ -2227,7 +2308,7 @@ ice_collect_result_idx(struct ice_aqc_recipe_data_elem *buf,
 */
static int
ice_get_recp_frm_fw(struct ice_hw *hw, struct ice_sw_recipe *recps, u8 rid,
		    bool *refresh_required)
		    bool *refresh_required, bool is_add)
{
	DECLARE_BITMAP(result_bm, ICE_MAX_FV_WORDS);
	struct ice_aqc_recipe_data_elem *tmp;
@@ -2344,8 +2425,12 @@ ice_get_recp_frm_fw(struct ice_hw *hw, struct ice_sw_recipe *recps, u8 rid,
			recps[idx].chain_idx = ICE_INVAL_CHAIN_IND;
		}

		if (!is_root)
		if (!is_root) {
			if (hw->recp_reuse && is_add)
				recps[idx].recp_created = true;

			continue;
		}

		/* Only do the following for root recipes entries */
		memcpy(recps[idx].r_bitmap, root_bufs.recipe_bitmap,
@@ -2369,6 +2454,7 @@ ice_get_recp_frm_fw(struct ice_hw *hw, struct ice_sw_recipe *recps, u8 rid,

	/* Copy result indexes */
	bitmap_copy(recps[rid].res_idxs, result_bm, ICE_MAX_FV_WORDS);
	if (is_add)
		recps[rid].recp_created = true;

err_unroll:
@@ -4653,12 +4739,13 @@ static struct ice_protocol_entry ice_prot_id_tbl[ICE_PROTOCOL_LAST] = {
 * @hw: pointer to the hardware structure
 * @lkup_exts: extension sequence to match
 * @rinfo: information regarding the rule e.g. priority and action info
 * @is_add: flag of adding recipe
 *
 * Returns index of matching recipe, or ICE_MAX_NUM_RECIPES if not found.
 */
static u16
ice_find_recp(struct ice_hw *hw, struct ice_prot_lkup_ext *lkup_exts,
	      const struct ice_adv_rule_info *rinfo)
	      const struct ice_adv_rule_info *rinfo, bool is_add)
{
	bool refresh_required = true;
	struct ice_sw_recipe *recp;
@@ -4672,11 +4759,12 @@ ice_find_recp(struct ice_hw *hw, struct ice_prot_lkup_ext *lkup_exts,
		 * entry update it in our SW bookkeeping and continue with the
		 * matching.
		 */
		if (!recp[i].recp_created)
		if (hw->recp_reuse) {
			if (ice_get_recp_frm_fw(hw,
						hw->switch_info->recp_list, i,
						&refresh_required))
						&refresh_required, is_add))
				continue;
		}

		/* Skip inverse action recipes */
		if (recp[i].root_buf && recp[i].root_buf->content.act_ctrl &
@@ -5360,6 +5448,49 @@ ice_get_compat_fv_bitmap(struct ice_hw *hw, struct ice_adv_rule_info *rinfo,
	ice_get_sw_fv_bitmap(hw, prof_type, bm);
}

/**
 * ice_subscribe_recipe - subscribe to an existing recipe
 * @hw: pointer to the hardware structure
 * @rid: recipe ID to subscribe to
 *
 * Return: 0 on success, and others on error
 */
static int ice_subscribe_recipe(struct ice_hw *hw, u16 rid)
{
	DEFINE_RAW_FLEX(struct ice_aqc_alloc_free_res_elem, sw_buf, elem, 1);
	u16 buf_len = __struct_size(sw_buf);
	u16 res_type;
	int status;

	/* Prepare buffer to allocate resource */
	sw_buf->num_elems = cpu_to_le16(1);
	res_type = FIELD_PREP(ICE_AQC_RES_TYPE_M, ICE_AQC_RES_TYPE_RECIPE) |
		   ICE_AQC_RES_TYPE_FLAG_SUBSCRIBE_SHARED |
		   ICE_AQC_RES_TYPE_FLAG_SUBSCRIBE_CTL;
	sw_buf->res_type = cpu_to_le16(res_type);

	sw_buf->elem[0].e.sw_resp = cpu_to_le16(rid);

	status = ice_aq_alloc_free_res(hw, sw_buf, buf_len,
				       ice_aqc_opc_alloc_res);

	return status;
}

/**
 * ice_subscribable_recp_shared - share an existing subscribable recipe
 * @hw: pointer to the hardware structure
 * @rid: recipe ID to subscribe to
 */
static void ice_subscribable_recp_shared(struct ice_hw *hw, u16 rid)
{
	struct ice_sw_recipe *recps = hw->switch_info->recp_list;
	u16 sub_rid;

	for_each_set_bit(sub_rid, recps[rid].r_bitmap, ICE_MAX_NUM_RECIPES)
		ice_subscribe_recipe(hw, sub_rid);
}

/**
 * ice_add_adv_recipe - Add an advanced recipe that is not part of the default
 * @hw: pointer to hardware structure
@@ -5382,6 +5513,7 @@ ice_add_adv_recipe(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups,
	struct ice_sw_fv_list_entry *tmp;
	struct ice_sw_recipe *rm;
	int status = 0;
	u16 rid_tmp;
	u8 i;

	if (!lkups_cnt)
@@ -5459,10 +5591,14 @@ ice_add_adv_recipe(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups,
	}

	/* Look for a recipe which matches our requested fv / mask list */
	*rid = ice_find_recp(hw, lkup_exts, rinfo);
	if (*rid < ICE_MAX_NUM_RECIPES)
	*rid = ice_find_recp(hw, lkup_exts, rinfo, true);
	if (*rid < ICE_MAX_NUM_RECIPES) {
		/* Success if found a recipe that match the existing criteria */
		if (hw->recp_reuse)
			ice_subscribable_recp_shared(hw, *rid);

		goto err_unroll;
	}

	rm->tun_type = rinfo->tun_type;
	/* Recipe we need does not exist, add a recipe */
@@ -5481,14 +5617,14 @@ ice_add_adv_recipe(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups,
		status = ice_aq_get_recipe_to_profile(hw, fvit->profile_id,
						      &recp_assoc, NULL);
		if (status)
			goto err_unroll;
			goto err_free_recipe;

		bitmap_from_arr64(r_bitmap, &recp_assoc, ICE_MAX_NUM_RECIPES);
		bitmap_or(r_bitmap, r_bitmap, rm->r_bitmap,
			  ICE_MAX_NUM_RECIPES);
		status = ice_acquire_change_lock(hw, ICE_RES_WRITE);
		if (status)
			goto err_unroll;
			goto err_free_recipe;

		bitmap_to_arr64(&recp_assoc, r_bitmap, ICE_MAX_NUM_RECIPES);
		status = ice_aq_map_recipe_to_profile(hw, fvit->profile_id,
@@ -5496,7 +5632,7 @@ ice_add_adv_recipe(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups,
		ice_release_change_lock(hw);

		if (status)
			goto err_unroll;
			goto err_free_recipe;

		/* Update profile to recipe bitmap array */
		bitmap_copy(profile_to_recipe[fvit->profile_id], r_bitmap,
@@ -5510,6 +5646,16 @@ ice_add_adv_recipe(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups,
	*rid = rm->root_rid;
	memcpy(&hw->switch_info->recp_list[*rid].lkup_exts, lkup_exts,
	       sizeof(*lkup_exts));
	goto err_unroll;

err_free_recipe:
	if (hw->recp_reuse) {
		for_each_set_bit(rid_tmp, rm->r_bitmap, ICE_MAX_NUM_RECIPES) {
			if (!ice_free_recipe_res(hw, rid_tmp))
				clear_bit(rid_tmp, rm->r_bitmap);
		}
	}

err_unroll:
	list_for_each_entry_safe(r_entry, r_tmp, &rm->rg_list, l_entry) {
		list_del(&r_entry->l_entry);
@@ -6529,7 +6675,7 @@ ice_rem_adv_rule(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups,
			return -EIO;
	}

	rid = ice_find_recp(hw, &lkup_exts, rinfo);
	rid = ice_find_recp(hw, &lkup_exts, rinfo, false);
	/* If did not find a recipe that match the existing criteria */
	if (rid == ICE_MAX_NUM_RECIPES)
		return -EINVAL;
@@ -6573,14 +6719,21 @@ ice_rem_adv_rule(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups,
					 ice_aqc_opc_remove_sw_rules, NULL);
		if (!status || status == -ENOENT) {
			struct ice_switch_info *sw = hw->switch_info;
			struct ice_sw_recipe *r_list = sw->recp_list;

			mutex_lock(rule_lock);
			list_del(&list_elem->list_entry);
			devm_kfree(ice_hw_to_dev(hw), list_elem->lkups);
			devm_kfree(ice_hw_to_dev(hw), list_elem);
			mutex_unlock(rule_lock);
			if (list_empty(&sw->recp_list[rid].filt_rules))
				sw->recp_list[rid].adv_rule = false;
			if (list_empty(&r_list[rid].filt_rules)) {
				r_list[rid].adv_rule = false;

				/* All rules for this recipe are now removed */
				if (hw->recp_reuse)
					ice_release_recipe_res(hw,
							       &r_list[rid]);
			}
		}
		kfree(s_rule);
	}
+1 −0
Original line number Diff line number Diff line
@@ -432,5 +432,6 @@ ice_aq_get_recipe_to_profile(struct ice_hw *hw, u32 profile_id, u64 *r_assoc,
int
ice_aq_map_recipe_to_profile(struct ice_hw *hw, u32 profile_id, u64 r_assoc,
			     struct ice_sq_cd *cd);
void ice_init_chk_recipe_reuse_support(struct ice_hw *hw);

#endif /* _ICE_SWITCH_H_ */
+2 −0
Original line number Diff line number Diff line
@@ -848,6 +848,8 @@ struct ice_hw {

	u16 max_burst_size;	/* driver sets this value */

	u8 recp_reuse:1;	/* indicates whether FW supports recipe reuse */

	/* Tx Scheduler values */
	u8 num_tx_sched_layers;
	u8 num_tx_sched_phys_layers;