ice: Add switch recipe reusing feature
authorSteven Zou <steven.zou@intel.com>
Thu, 8 Feb 2024 03:18:37 +0000 (11:18 +0800)
committerTony Nguyen <anthony.l.nguyen@intel.com>
Mon, 1 Apr 2024 15:58:09 +0000 (08:58 -0700)
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: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Reviewed-by: Andrii Staikov <andrii.staikov@intel.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: Steven Zou <steven.zou@intel.com>
Tested-by: Mayank Sharma <mayank.sharma@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
drivers/net/ethernet/intel/ice/ice_common.c
drivers/net/ethernet/intel/ice/ice_switch.c
drivers/net/ethernet/intel/ice/ice_switch.h
drivers/net/ethernet/intel/ice/ice_type.h

index 1f3e7a6..540c0bd 100644 (file)
@@ -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
 
index db4b284..dc5a1ef 100644 (file)
@@ -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:
index 606c241..94d6670 100644 (file)
@@ -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,7 +2454,8 @@ 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);
-       recps[rid].recp_created = true;
+       if (is_add)
+               recps[rid].recp_created = true;
 
 err_unroll:
        kfree(tmp);
@@ -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);
        }
index 322ef81..ad98e98 100644 (file)
@@ -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_ */
index 64b8e7e..08ec5ef 100644 (file)
@@ -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;