+/**
+ * ice_alloc_res_lg_act - add large action resource
+ * @hw: pointer to the hardware structure
+ * @l_id: large action ID to fill it in
+ * @num_acts: number of actions to hold with a large action entry
+ */
+static enum ice_status
+ice_alloc_res_lg_act(struct ice_hw *hw, u16 *l_id, u16 num_acts)
+{
+ struct ice_aqc_alloc_free_res_elem *sw_buf;
+ enum ice_status status;
+ u16 buf_len;
+
+ if (num_acts > ICE_MAX_LG_ACT || num_acts == 0)
+ return ICE_ERR_PARAM;
+
+ /* Allocate resource for large action */
+ buf_len = sizeof(*sw_buf);
+ sw_buf = (struct ice_aqc_alloc_free_res_elem *)
+ ice_malloc(hw, buf_len);
+ if (!sw_buf)
+ return ICE_ERR_NO_MEMORY;
+
+ sw_buf->num_elems = CPU_TO_LE16(1);
+
+ /* If num_acts is 1, use ICE_AQC_RES_TYPE_WIDE_TABLE_1.
+ * If num_acts is 2, use ICE_AQC_RES_TYPE_WIDE_TABLE_3.
+ * If num_acts is greater than 2, then use
+ * ICE_AQC_RES_TYPE_WIDE_TABLE_4.
+ * The num_acts cannot exceed 4. This was ensured at the
+ * beginning of the function.
+ */
+ if (num_acts == 1)
+ sw_buf->res_type = CPU_TO_LE16(ICE_AQC_RES_TYPE_WIDE_TABLE_1);
+ else if (num_acts == 2)
+ sw_buf->res_type = CPU_TO_LE16(ICE_AQC_RES_TYPE_WIDE_TABLE_2);
+ else
+ sw_buf->res_type = CPU_TO_LE16(ICE_AQC_RES_TYPE_WIDE_TABLE_4);
+
+ status = ice_aq_alloc_free_res(hw, 1, sw_buf, buf_len,
+ ice_aqc_opc_alloc_res, NULL);
+ if (!status)
+ *l_id = LE16_TO_CPU(sw_buf->elem[0].e.sw_resp);
+
+ ice_free(hw, sw_buf);
+ return status;
+}
+
+/**
+ * ice_add_mac_with_sw_marker - add filter with sw marker
+ * @hw: pointer to the hardware structure
+ * @f_info: filter info structure containing the MAC filter information
+ * @sw_marker: sw marker to tag the Rx descriptor with
+ */
+enum ice_status
+ice_add_mac_with_sw_marker(struct ice_hw *hw, struct ice_fltr_info *f_info,
+ u16 sw_marker)
+{
+ struct ice_switch_info *sw = hw->switch_info;
+ struct ice_fltr_mgmt_list_entry *m_entry;
+ struct ice_fltr_list_entry fl_info;
+ struct LIST_HEAD_TYPE l_head;
+ struct ice_lock *rule_lock; /* Lock to protect filter rule list */
+ enum ice_status ret;
+ bool entry_exists;
+ u16 lg_act_id;
+
+ if (f_info->fltr_act != ICE_FWD_TO_VSI)
+ return ICE_ERR_PARAM;
+
+ if (f_info->lkup_type != ICE_SW_LKUP_MAC)
+ return ICE_ERR_PARAM;
+
+ if (sw_marker == ICE_INVAL_SW_MARKER_ID)
+ return ICE_ERR_PARAM;
+
+ if (!ice_is_vsi_valid(hw, f_info->vsi_handle))
+ return ICE_ERR_PARAM;
+ f_info->fwd_id.hw_vsi_id = ice_get_hw_vsi_num(hw, f_info->vsi_handle);
+
+ /* Add filter if it doesn't exist so then the adding of large
+ * action always results in update
+ */
+
+ INIT_LIST_HEAD(&l_head);
+ fl_info.fltr_info = *f_info;
+ LIST_ADD(&fl_info.list_entry, &l_head);
+
+ entry_exists = false;
+ ret = ice_add_mac(hw, &l_head);
+ if (ret == ICE_ERR_ALREADY_EXISTS)
+ entry_exists = true;
+ else if (ret)
+ return ret;
+
+ rule_lock = &sw->recp_list[ICE_SW_LKUP_MAC].filt_rule_lock;
+ ice_acquire_lock(rule_lock);
+ /* Get the book keeping entry for the filter */
+ m_entry = ice_find_rule_entry(hw, ICE_SW_LKUP_MAC, f_info);
+ if (!m_entry)
+ goto exit_error;
+
+ /* If counter action was enabled for this rule then don't enable
+ * sw marker large action
+ */
+ if (m_entry->counter_index != ICE_INVAL_COUNTER_ID) {
+ ret = ICE_ERR_PARAM;
+ goto exit_error;
+ }
+
+ /* if same marker was added before */
+ if (m_entry->sw_marker_id == sw_marker) {
+ ret = ICE_ERR_ALREADY_EXISTS;
+ goto exit_error;
+ }
+
+ /* Allocate a hardware table entry to hold large act. Three actions
+ * for marker based large action
+ */
+ ret = ice_alloc_res_lg_act(hw, &lg_act_id, 3);
+ if (ret)
+ goto exit_error;
+
+ if (lg_act_id == ICE_INVAL_LG_ACT_INDEX)
+ goto exit_error;
+
+ /* Update the switch rule to add the marker action */
+ ret = ice_add_marker_act(hw, m_entry, sw_marker, lg_act_id);
+ if (!ret) {
+ ice_release_lock(rule_lock);
+ return ret;
+ }
+
+exit_error:
+ ice_release_lock(rule_lock);
+ /* only remove entry if it did not exist previously */
+ if (!entry_exists)
+ ret = ice_remove_mac(hw, &l_head);
+
+ return ret;
+}
+
+/**
+ * ice_add_mac_with_counter - add filter with counter enabled
+ * @hw: pointer to the hardware structure
+ * @f_info: pointer to filter info structure containing the MAC filter
+ * information
+ */
+enum ice_status
+ice_add_mac_with_counter(struct ice_hw *hw, struct ice_fltr_info *f_info)
+{
+ struct ice_switch_info *sw = hw->switch_info;
+ struct ice_fltr_mgmt_list_entry *m_entry;
+ struct ice_fltr_list_entry fl_info;
+ struct LIST_HEAD_TYPE l_head;
+ struct ice_lock *rule_lock; /* Lock to protect filter rule list */
+ enum ice_status ret;
+ bool entry_exist;
+ u16 counter_id;
+ u16 lg_act_id;
+
+ if (f_info->fltr_act != ICE_FWD_TO_VSI)
+ return ICE_ERR_PARAM;
+
+ if (f_info->lkup_type != ICE_SW_LKUP_MAC)
+ return ICE_ERR_PARAM;
+
+ if (!ice_is_vsi_valid(hw, f_info->vsi_handle))
+ return ICE_ERR_PARAM;
+ f_info->fwd_id.hw_vsi_id = ice_get_hw_vsi_num(hw, f_info->vsi_handle);
+
+ entry_exist = false;
+
+ rule_lock = &sw->recp_list[ICE_SW_LKUP_MAC].filt_rule_lock;
+
+ /* Add filter if it doesn't exist so then the adding of large
+ * action always results in update
+ */
+ INIT_LIST_HEAD(&l_head);
+
+ fl_info.fltr_info = *f_info;
+ LIST_ADD(&fl_info.list_entry, &l_head);
+
+ ret = ice_add_mac(hw, &l_head);
+ if (ret == ICE_ERR_ALREADY_EXISTS)
+ entry_exist = true;
+ else if (ret)
+ return ret;
+
+ ice_acquire_lock(rule_lock);
+ m_entry = ice_find_rule_entry(hw, ICE_SW_LKUP_MAC, f_info);
+ if (!m_entry) {
+ ret = ICE_ERR_BAD_PTR;
+ goto exit_error;
+ }
+
+ /* Don't enable counter for a filter for which sw marker was enabled */
+ if (m_entry->sw_marker_id != ICE_INVAL_SW_MARKER_ID) {
+ ret = ICE_ERR_PARAM;
+ goto exit_error;
+ }
+
+ /* If a counter was already enabled then don't need to add again */
+ if (m_entry->counter_index != ICE_INVAL_COUNTER_ID) {
+ ret = ICE_ERR_ALREADY_EXISTS;
+ goto exit_error;
+ }
+
+ /* Allocate a hardware table entry to VLAN counter */
+ ret = ice_alloc_vlan_res_counter(hw, &counter_id);
+ if (ret)
+ goto exit_error;
+
+ /* Allocate a hardware table entry to hold large act. Two actions for
+ * counter based large action
+ */
+ ret = ice_alloc_res_lg_act(hw, &lg_act_id, 2);
+ if (ret)
+ goto exit_error;
+
+ if (lg_act_id == ICE_INVAL_LG_ACT_INDEX)
+ goto exit_error;
+
+ /* Update the switch rule to add the counter action */
+ ret = ice_add_counter_act(hw, m_entry, counter_id, lg_act_id);
+ if (!ret) {
+ ice_release_lock(rule_lock);
+ return ret;
+ }
+
+exit_error:
+ ice_release_lock(rule_lock);
+ /* only remove entry if it did not exist previously */
+ if (!entry_exist)
+ ret = ice_remove_mac(hw, &l_head);
+
+ return ret;
+}
+