@@ -124,3 +124,710 @@ void ice_dcf_set_state(struct ice_pf *pf, enum ice_dcf_state state)
pf->dcf.state = state;
}
+
+/**
+ * ice_dcf_rm_sw_rule_to_vsi - remove switch rule of "forward to VSI"
+ * @pf: pointer to the PF struct
+ * @s_entry: pointer to switch rule entry to remove
+ */
+static int
+ice_dcf_rm_sw_rule_to_vsi(struct ice_pf *pf,
+ struct ice_dcf_sw_rule_entry *s_entry)
+{
+ struct ice_aqc_sw_rules_elem *s_rule;
+ enum ice_status status;
+
+ s_rule = kzalloc(ICE_SW_RULE_RX_TX_NO_HDR_SIZE, GFP_KERNEL);
+ if (!s_rule)
+ return -ENOMEM;
+
+ s_rule->type = cpu_to_le16(ICE_AQC_SW_RULES_T_LKUP_RX);
+ s_rule->pdata.lkup_tx_rx.act = 0;
+ s_rule->pdata.lkup_tx_rx.hdr_len = 0;
+ s_rule->pdata.lkup_tx_rx.index = cpu_to_le16(s_entry->rule_id);
+ status = ice_aq_sw_rules(&pf->hw, s_rule, ICE_SW_RULE_RX_TX_NO_HDR_SIZE,
+ 1, ice_aqc_opc_remove_sw_rules, NULL);
+ kfree(s_rule);
+ if (status)
+ return -EIO;
+
+ list_del(&s_entry->list_entry);
+ kfree(s_entry);
+ return 0;
+}
+
+/**
+ * ice_dcf_rm_sw_rule_to_vsi_list - remove switch rule of "forward to VSI list"
+ * @pf: pointer to the PF struct
+ * @s_entry: pointer to switch rule entry to remove
+ */
+static int
+ice_dcf_rm_sw_rule_to_vsi_list(struct ice_pf *pf,
+ struct ice_dcf_sw_rule_entry *s_entry)
+{
+ struct ice_dcf_vsi_list_info *vsi_list_info = s_entry->vsi_list_info;
+ struct ice_aqc_alloc_free_res_elem *res_buf;
+ struct ice_aqc_sw_rules_elem *s_rule;
+ enum ice_status status;
+ u16 rule_sz;
+ u16 vsi_id;
+ int i = 0;
+
+ if (!vsi_list_info)
+ return -EINVAL;
+
+ /* The VSI list is empty, it can be freed immediately */
+ if (!vsi_list_info->vsi_count)
+ goto free_vsi_list;
+
+ rule_sz = ICE_SW_RULE_VSI_LIST_SIZE(vsi_list_info->vsi_count);
+ s_rule = kzalloc(rule_sz, GFP_KERNEL);
+ if (!s_rule)
+ return -ENOMEM;
+
+ 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_info->list_id);
+ s_rule->pdata.vsi_list.number_vsi =
+ cpu_to_le16(vsi_list_info->vsi_count);
+ for_each_set_bit(vsi_id, vsi_list_info->hw_vsi_map, ICE_HW_VSI_ID_MAX)
+ s_rule->pdata.vsi_list.vsi[i++] = cpu_to_le16(vsi_id);
+
+ bitmap_zero(vsi_list_info->hw_vsi_map, ICE_HW_VSI_ID_MAX);
+ vsi_list_info->vsi_count = 0;
+
+ status = ice_aq_sw_rules(&pf->hw, s_rule, rule_sz, 1,
+ ice_aqc_opc_update_sw_rules, NULL);
+ kfree(s_rule);
+ if (status)
+ return -EIO;
+
+free_vsi_list:
+ res_buf = kzalloc(sizeof(*res_buf), GFP_KERNEL);
+ if (!res_buf)
+ return -ENOMEM;
+
+ res_buf->res_type = cpu_to_le16(ICE_AQC_RES_TYPE_VSI_LIST_REP);
+ res_buf->num_elems = cpu_to_le16(1);
+ res_buf->elem[0].e.sw_resp = cpu_to_le16(vsi_list_info->list_id);
+ status = ice_aq_alloc_free_res(&pf->hw, 1, res_buf, sizeof(*res_buf),
+ ice_aqc_opc_free_res, NULL);
+ kfree(res_buf);
+ if (status)
+ return -EIO;
+
+ list_del(&vsi_list_info->list_entry);
+ kfree(vsi_list_info);
+ s_entry->vsi_list_info = NULL;
+
+ return ice_dcf_rm_sw_rule_to_vsi(pf, s_entry);
+}
+
+/**
+ * ice_dcf_rm_vsi_from_list - remove VSI from switch rule forward VSI list
+ * @pf: pointer to the PF struct
+ * @vsi_list_info: pointer to the VSI list info
+ * @hw_vsi_id: the Hardware VSI number
+ */
+static int
+ice_dcf_rm_vsi_from_list(struct ice_pf *pf,
+ struct ice_dcf_vsi_list_info *vsi_list_info,
+ u16 hw_vsi_id)
+{
+ struct ice_aqc_sw_rules_elem *s_rule;
+ enum ice_status status;
+
+ if (!vsi_list_info || !vsi_list_info->vsi_count ||
+ !test_bit(hw_vsi_id, vsi_list_info->hw_vsi_map))
+ return -ENOENT;
+
+ s_rule = kzalloc(ICE_SW_RULE_VSI_LIST_SIZE(1), GFP_KERNEL);
+ if (!s_rule)
+ return -ENOMEM;
+
+ 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_info->list_id);
+ s_rule->pdata.vsi_list.number_vsi = cpu_to_le16(1);
+ s_rule->pdata.vsi_list.vsi[0] = cpu_to_le16(hw_vsi_id);
+ status = ice_aq_sw_rules(&pf->hw, s_rule,
+ ICE_SW_RULE_VSI_LIST_SIZE(1), 1,
+ ice_aqc_opc_update_sw_rules, NULL);
+ kfree(s_rule);
+ if (status)
+ return -EIO;
+
+ /* When the VF resets gracefully, it should keep the VSI list and its
+ * rule, just clears the VSI from list, so that the DCF can replay the
+ * rule by updating this VF to list successfully.
+ */
+ vsi_list_info->vsi_count--;
+ clear_bit(hw_vsi_id, vsi_list_info->hw_vsi_map);
+
+ return 0;
+}
+
+/**
+ * ice_rm_dcf_sw_vsi_rule - remove switch rules added by DCF to VSI
+ * @pf: pointer to the PF struct
+ * @hw_vsi_id: hardware VSI ID of the VF
+ */
+void ice_rm_dcf_sw_vsi_rule(struct ice_pf *pf, u16 hw_vsi_id)
+{
+ struct ice_dcf_sw_rule_entry *s_entry, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(s_entry, tmp, &pf->dcf.sw_rule_head,
+ list_entry)
+ if (s_entry->fltr_act == ICE_FWD_TO_VSI_LIST) {
+ ret = ice_dcf_rm_vsi_from_list(pf,
+ s_entry->vsi_list_info,
+ hw_vsi_id);
+ if (ret && ret != -ENOENT)
+ dev_err(ice_pf_to_dev(pf),
+ "Failed to remove VSI %u from VSI list : %d\n",
+ hw_vsi_id, ret);
+ } else if (s_entry->fwd_id.hw_vsi_id == hw_vsi_id) {
+ ret = ice_dcf_rm_sw_rule_to_vsi(pf, s_entry);
+ if (ret)
+ dev_err(ice_pf_to_dev(pf),
+ "Failed to remove VSI %u switch rule : %d\n",
+ hw_vsi_id, ret);
+ }
+}
+
+/**
+ * ice_dcf_init_sw_rule_mgmt - initializes DCF rule filter mgmt struct
+ * @pf: pointer to the PF struct
+ */
+void ice_dcf_init_sw_rule_mgmt(struct ice_pf *pf)
+{
+ INIT_LIST_HEAD(&pf->dcf.sw_rule_head);
+ INIT_LIST_HEAD(&pf->dcf.vsi_list_info_head);
+}
+
+/**
+ * ice_rm_all_dcf_sw_rules - remove switch rules configured by DCF
+ * @pf: pointer to the PF struct
+ */
+void ice_rm_all_dcf_sw_rules(struct ice_pf *pf)
+{
+ struct ice_dcf_vsi_list_info *vsi_list_info, *list_info_tmp;
+ struct ice_dcf_sw_rule_entry *sw_rule, *rule_tmp;
+ u16 rule_id, list_id;
+ int ret;
+
+ list_for_each_entry_safe(sw_rule, rule_tmp, &pf->dcf.sw_rule_head,
+ list_entry)
+ if (sw_rule->fltr_act == ICE_FWD_TO_VSI_LIST) {
+ list_id = sw_rule->fwd_id.vsi_list_id;
+ rule_id = sw_rule->rule_id;
+ ret = ice_dcf_rm_sw_rule_to_vsi_list(pf, sw_rule);
+ if (ret)
+ dev_err(ice_pf_to_dev(pf),
+ "Failed to remove switch rule 0x%04x with list id %u : %d\n",
+ rule_id, list_id, ret);
+ } else {
+ rule_id = sw_rule->rule_id;
+ ret = ice_dcf_rm_sw_rule_to_vsi(pf, sw_rule);
+ if (ret)
+ dev_err(ice_pf_to_dev(pf),
+ "Failed to remove switch rule 0x%04x : %d\n",
+ rule_id, ret);
+ }
+
+ /* clears rule filter management data if AdminQ command has error */
+ list_for_each_entry_safe(vsi_list_info, list_info_tmp,
+ &pf->dcf.vsi_list_info_head,
+ list_entry) {
+ list_del(&vsi_list_info->list_entry);
+ kfree(vsi_list_info);
+ }
+
+ list_for_each_entry_safe(sw_rule, rule_tmp, &pf->dcf.sw_rule_head,
+ list_entry) {
+ list_del(&sw_rule->list_entry);
+ kfree(sw_rule);
+ }
+}
+
+/**
+ * ice_dcf_find_vsi_list_info - find the VSI list by ID
+ * @pf: pointer to the PF info
+ * @vsi_list_id: VSI list ID
+ */
+static struct ice_dcf_vsi_list_info *
+ice_dcf_find_vsi_list_info(struct ice_pf *pf, u16 vsi_list_id)
+{
+ struct ice_dcf_vsi_list_info *list_info;
+
+ list_for_each_entry(list_info, &pf->dcf.vsi_list_info_head, list_entry)
+ if (list_info->list_id == vsi_list_id)
+ return list_info;
+
+ return NULL;
+}
+
+/**
+ * ice_dcf_add_vsi_id - add new VSI ID into list
+ * @vsi_list_info: pointer to the VSI list info
+ * @hw_vsi_id: the VSI ID
+ */
+static void
+ice_dcf_add_vsi_id(struct ice_dcf_vsi_list_info *vsi_list_info, u16 hw_vsi_id)
+{
+ if (!test_and_set_bit(hw_vsi_id, vsi_list_info->hw_vsi_map))
+ vsi_list_info->vsi_count++;
+}
+
+/**
+ * ice_dcf_del_vsi_id - delete the VSI ID from list
+ * @vsi_list_info: pointer to the VSI list info
+ * @hw_vsi_id: the VSI ID
+ */
+static void
+ice_dcf_del_vsi_id(struct ice_dcf_vsi_list_info *vsi_list_info, u16 hw_vsi_id)
+{
+ if (test_and_clear_bit(hw_vsi_id, vsi_list_info->hw_vsi_map))
+ vsi_list_info->vsi_count--;
+}
+
+/**
+ * ice_dcf_parse_alloc_vsi_list_res - parse the allocate VSI list resource
+ * @pf: pointer to the PF info
+ * @res: pointer to the VSI list resource
+ */
+static enum virtchnl_status_code
+ice_dcf_parse_alloc_vsi_list_res(struct ice_pf *pf,
+ struct ice_aqc_res_elem *res)
+{
+ struct ice_dcf_vsi_list_info *vsi_list_info;
+ u16 list_id = le16_to_cpu(res->e.sw_resp);
+
+ vsi_list_info = ice_dcf_find_vsi_list_info(pf, list_id);
+ if (vsi_list_info)
+ return VIRTCHNL_STATUS_SUCCESS;
+
+ vsi_list_info = kzalloc(sizeof(*vsi_list_info), GFP_KERNEL);
+ if (!vsi_list_info)
+ return VIRTCHNL_STATUS_ERR_NO_MEMORY;
+
+ vsi_list_info->list_id = list_id;
+ list_add(&vsi_list_info->list_entry, &pf->dcf.vsi_list_info_head);
+
+ return VIRTCHNL_STATUS_SUCCESS;
+}
+
+/**
+ * ice_dcf_parse_free_vsi_list_res - parse the free VSI list resource
+ * @pf: pointer to the PF info
+ * @res: pointer to the VSI list resource
+ */
+static enum virtchnl_status_code
+ice_dcf_parse_free_vsi_list_res(struct ice_pf *pf,
+ struct ice_aqc_res_elem *res)
+{
+ struct ice_dcf_vsi_list_info *vsi_list_info;
+ u16 list_id = le16_to_cpu(res->e.sw_resp);
+
+ vsi_list_info = ice_dcf_find_vsi_list_info(pf, list_id);
+ if (!vsi_list_info)
+ return VIRTCHNL_STATUS_ERR_PARAM;
+
+ if (vsi_list_info->vsi_count)
+ dev_warn(ice_pf_to_dev(pf),
+ "VSI list %u still has %u VSIs to be removed!\n",
+ list_id, vsi_list_info->vsi_count);
+
+ if (vsi_list_info->sw_rule)
+ vsi_list_info->sw_rule->vsi_list_info = NULL;
+
+ list_del(&vsi_list_info->list_entry);
+ kfree(vsi_list_info);
+
+ return VIRTCHNL_STATUS_SUCCESS;
+}
+
+/**
+ * ice_dcf_set_vsi_list - set the VSI to VSI list
+ * @pf: pointer to the PF info
+ * @vsi_list: pointer to the VSI ID list to be set
+ */
+static enum virtchnl_status_code
+ice_dcf_set_vsi_list(struct ice_pf *pf, struct ice_aqc_sw_rules_elem *vsi_list)
+{
+ struct ice_dcf_vsi_list_info *vsi_list_info;
+ int i;
+
+ vsi_list_info = ice_dcf_find_vsi_list_info(pf,
+ le16_to_cpu(vsi_list->pdata.vsi_list.index));
+ if (!vsi_list_info)
+ return VIRTCHNL_STATUS_ERR_PARAM;
+
+ for (i = 0; i < le16_to_cpu(vsi_list->pdata.vsi_list.number_vsi); i++)
+ ice_dcf_add_vsi_id(vsi_list_info,
+ le16_to_cpu(vsi_list->pdata.vsi_list.vsi[i]));
+
+ return VIRTCHNL_STATUS_SUCCESS;
+}
+
+/**
+ * ice_dcf_clear_vsi_list - clear the VSI from VSI list
+ * @pf: pointer to the PF info
+ * @vsi_list: pointer to the VSI ID list to be cleared
+ */
+static enum virtchnl_status_code
+ice_dcf_clear_vsi_list(struct ice_pf *pf, struct ice_aqc_sw_rules_elem *vsi_list)
+{
+ struct ice_dcf_vsi_list_info *vsi_list_info;
+ int i;
+
+ vsi_list_info = ice_dcf_find_vsi_list_info(pf,
+ le16_to_cpu(vsi_list->pdata.vsi_list.index));
+ if (!vsi_list_info)
+ return VIRTCHNL_STATUS_ERR_PARAM;
+
+ for (i = 0; i < le16_to_cpu(vsi_list->pdata.vsi_list.number_vsi); i++)
+ ice_dcf_del_vsi_id(vsi_list_info,
+ le16_to_cpu(vsi_list->pdata.vsi_list.vsi[i]));
+
+ return VIRTCHNL_STATUS_SUCCESS;
+}
+
+/**
+ * ice_dcf_find_sw_rule - find the switch rule by ID
+ * @pf: pointer to the PF info
+ * @rule_id: switch rule ID
+ */
+static struct ice_dcf_sw_rule_entry *
+ice_dcf_find_sw_rule(struct ice_pf *pf, u16 rule_id)
+{
+ struct ice_dcf_sw_rule_entry *sw_rule;
+
+ list_for_each_entry(sw_rule, &pf->dcf.sw_rule_head, list_entry)
+ if (sw_rule->rule_id == rule_id)
+ return sw_rule;
+
+ return NULL;
+}
+
+/**
+ * ice_dcf_parse_add_sw_rule_data - parse the add switch rule data
+ * @pf: pointer to the PF info
+ * @lkup: pointer to the add switch rule data
+ */
+static enum virtchnl_status_code
+ice_dcf_parse_add_sw_rule_data(struct ice_pf *pf, struct ice_aqc_sw_rules_elem *lkup)
+{
+ struct ice_dcf_sw_rule_entry *sw_rule;
+ u32 act;
+
+ sw_rule = kzalloc(sizeof(*sw_rule), GFP_KERNEL);
+ if (!sw_rule)
+ return VIRTCHNL_STATUS_ERR_NO_MEMORY;
+
+ act = le32_to_cpu(lkup->pdata.lkup_tx_rx.act);
+ sw_rule->fltr_act = ICE_FWD_TO_VSI;
+ sw_rule->fwd_id.hw_vsi_id = (act & ICE_SINGLE_ACT_VSI_ID_M) >>
+ ICE_SINGLE_ACT_VSI_ID_S;
+ sw_rule->rule_id = le16_to_cpu(lkup->pdata.lkup_tx_rx.index);
+
+ list_add(&sw_rule->list_entry, &pf->dcf.sw_rule_head);
+
+ return VIRTCHNL_STATUS_SUCCESS;
+}
+
+/**
+ * ice_dcf_parse_updt_sw_rule_data - parse the update switch rule data
+ * @pf: pointer to the PF info
+ * @lkup: pointer to the update switch rule data
+ */
+static enum virtchnl_status_code
+ice_dcf_parse_updt_sw_rule_data(struct ice_pf *pf, struct ice_aqc_sw_rules_elem *lkup)
+{
+ struct ice_dcf_vsi_list_info *vsi_list_info;
+ struct ice_dcf_sw_rule_entry *sw_rule;
+ u16 vsi_list_id, rule_id;
+ u32 act;
+
+ rule_id = le16_to_cpu(lkup->pdata.lkup_tx_rx.index);
+ sw_rule = ice_dcf_find_sw_rule(pf, rule_id);
+ if (!sw_rule)
+ return VIRTCHNL_STATUS_ERR_PARAM;
+
+ act = le32_to_cpu(lkup->pdata.lkup_tx_rx.act);
+ if (!(act & ICE_SINGLE_ACT_VSI_LIST)) {
+ u16 vsi_hw_id = (act & ICE_SINGLE_ACT_VSI_ID_M) >>
+ ICE_SINGLE_ACT_VSI_ID_S;
+
+ sw_rule->fltr_act = ICE_FWD_TO_VSI;
+ sw_rule->fwd_id.hw_vsi_id = vsi_hw_id;
+
+ return VIRTCHNL_STATUS_SUCCESS;
+ }
+
+ vsi_list_id = (act & ICE_SINGLE_ACT_VSI_LIST_ID_M) >>
+ ICE_SINGLE_ACT_VSI_LIST_ID_S;
+ if (sw_rule->vsi_list_info) {
+ if (sw_rule->vsi_list_info->list_id == vsi_list_id)
+ return VIRTCHNL_STATUS_SUCCESS;
+
+ dev_err(ice_pf_to_dev(pf),
+ "The switch rule 0x%04x is running on VSI list %u\n",
+ rule_id, sw_rule->vsi_list_info->list_id);
+ return VIRTCHNL_STATUS_ERR_PARAM;
+ }
+
+ vsi_list_info = ice_dcf_find_vsi_list_info(pf, vsi_list_id);
+ if (!vsi_list_info) {
+ dev_err(ice_pf_to_dev(pf),
+ "No VSI list %u found to bind the switch rule 0x%04x\n",
+ vsi_list_id, rule_id);
+ return VIRTCHNL_STATUS_ERR_PARAM;
+ }
+
+ if (vsi_list_info->sw_rule) {
+ if (vsi_list_info->sw_rule->rule_id == rule_id)
+ return VIRTCHNL_STATUS_SUCCESS;
+
+ dev_err(ice_pf_to_dev(pf),
+ "The VSI list %u is running on switch rule 0x%04x\n",
+ vsi_list_id, vsi_list_info->sw_rule->rule_id);
+ return VIRTCHNL_STATUS_ERR_PARAM;
+ }
+
+ vsi_list_info->sw_rule = sw_rule;
+
+ sw_rule->fltr_act = ICE_FWD_TO_VSI_LIST;
+ sw_rule->fwd_id.vsi_list_id = vsi_list_id;
+ sw_rule->vsi_list_info = vsi_list_info;
+
+ return VIRTCHNL_STATUS_SUCCESS;
+}
+
+/**
+ * ice_dcf_parse_rm_sw_rule_data - parse the remove switch rule data
+ * @pf: pointer to the PF info
+ * @lkup: pointer to the remove switch rule data
+ */
+static enum virtchnl_status_code
+ice_dcf_parse_rm_sw_rule_data(struct ice_pf *pf, struct ice_aqc_sw_rules_elem *lkup)
+{
+ u16 rule_id = le16_to_cpu(lkup->pdata.lkup_tx_rx.index);
+ struct ice_dcf_sw_rule_entry *sw_rule, *tmp;
+
+ list_for_each_entry_safe(sw_rule, tmp, &pf->dcf.sw_rule_head,
+ list_entry)
+ if (sw_rule->rule_id == rule_id) {
+ list_del(&sw_rule->list_entry);
+ kfree(sw_rule);
+ }
+
+ return VIRTCHNL_STATUS_SUCCESS;
+}
+
+/**
+ * ice_dcf_handle_add_sw_rule_rsp - handle the add switch rule response
+ * @pf: pointer to the PF info
+ * @aq_buf: pointer to the add switch rule command buffer
+ */
+static enum virtchnl_status_code
+ice_dcf_handle_add_sw_rule_rsp(struct ice_pf *pf, u8 *aq_buf)
+{
+ enum virtchnl_status_code status = VIRTCHNL_STATUS_SUCCESS;
+ struct ice_aqc_sw_rules_elem *em =
+ (struct ice_aqc_sw_rules_elem *)aq_buf;
+ u16 type = le16_to_cpu(em->type);
+
+ if (type == ICE_AQC_SW_RULES_T_VSI_LIST_SET)
+ status = ice_dcf_set_vsi_list(pf, em);
+ else if (type == ICE_AQC_SW_RULES_T_LKUP_RX)
+ status = ice_dcf_parse_add_sw_rule_data(pf, em);
+
+ return status;
+}
+
+/**
+ * ice_dcf_handle_updt_sw_rule_rsp - handle the update switch rule response
+ * @pf: pointer to the PF info
+ * @aq_buf: pointer to the update switch rule command buffer
+ */
+static enum virtchnl_status_code
+ice_dcf_handle_updt_sw_rule_rsp(struct ice_pf *pf, u8 *aq_buf)
+{
+ enum virtchnl_status_code status = VIRTCHNL_STATUS_SUCCESS;
+ struct ice_aqc_sw_rules_elem *em =
+ (struct ice_aqc_sw_rules_elem *)aq_buf;
+ u16 type = le16_to_cpu(em->type);
+
+ if (type == ICE_AQC_SW_RULES_T_VSI_LIST_SET)
+ status = ice_dcf_set_vsi_list(pf, em);
+ else if (type == ICE_AQC_SW_RULES_T_VSI_LIST_CLEAR)
+ status = ice_dcf_clear_vsi_list(pf, em);
+ else if (type == ICE_AQC_SW_RULES_T_LKUP_RX)
+ status = ice_dcf_parse_updt_sw_rule_data(pf, em);
+
+ return status;
+}
+
+/**
+ * ice_dcf_handle_rm_sw_rule_rsp - handle the remove switch rule response
+ * @pf: pointer to the PF info
+ * @aq_buf: pointer to the remove switch rule command buffer
+ */
+static enum virtchnl_status_code
+ice_dcf_handle_rm_sw_rule_rsp(struct ice_pf *pf, u8 *aq_buf)
+{
+ enum virtchnl_status_code status = VIRTCHNL_STATUS_SUCCESS;
+ struct ice_aqc_sw_rules_elem *em =
+ (struct ice_aqc_sw_rules_elem *)aq_buf;
+ u16 type = le16_to_cpu(em->type);
+
+ if (type == ICE_AQC_SW_RULES_T_LKUP_RX)
+ status = ice_dcf_parse_rm_sw_rule_data(pf, em);
+
+ return status;
+}
+
+/**
+ * ice_dcf_handle_alloc_res_rsp - handle the allocate resource response
+ * @pf: pointer to the PF info
+ * @aq_buf: pointer to the allocate resource command buffer
+ */
+static enum virtchnl_status_code
+ice_dcf_handle_alloc_res_rsp(struct ice_pf *pf, u8 *aq_buf)
+{
+ enum virtchnl_status_code status = VIRTCHNL_STATUS_SUCCESS;
+ struct ice_aqc_alloc_free_res_elem *res_buf =
+ (struct ice_aqc_alloc_free_res_elem *)aq_buf;
+ u16 type = (le16_to_cpu(res_buf->res_type) &
+ ICE_AQC_RES_TYPE_M) >> ICE_AQC_RES_TYPE_S;
+
+ if (type == ICE_AQC_RES_TYPE_VSI_LIST_REP)
+ status = ice_dcf_parse_alloc_vsi_list_res(pf,
+ &res_buf->elem[0]);
+
+ return status;
+}
+
+/**
+ * ice_dcf_handle_free_res_rsp - handle the free resource response
+ * @pf: pointer to the PF info
+ * @aq_buf: pointer to the free resource command buffer
+ */
+static enum virtchnl_status_code
+ice_dcf_handle_free_res_rsp(struct ice_pf *pf, u8 *aq_buf)
+{
+ enum virtchnl_status_code status = VIRTCHNL_STATUS_SUCCESS;
+ struct ice_aqc_alloc_free_res_elem *res_buf =
+ (struct ice_aqc_alloc_free_res_elem *)aq_buf;
+ u16 type = (le16_to_cpu(res_buf->res_type) &
+ ICE_AQC_RES_TYPE_M) >> ICE_AQC_RES_TYPE_S;
+
+ if (type == ICE_AQC_RES_TYPE_VSI_LIST_REP)
+ status = ice_dcf_parse_free_vsi_list_res(pf,
+ &res_buf->elem[0]);
+
+ return status;
+}
+
+/**
+ * ice_dcf_post_aq_send_cmd - get the data from firmware successful response
+ * @pf: pointer to the PF info
+ * @aq_desc: descriptor describing the command
+ * @aq_buf: the AdminQ command buffer
+ */
+enum virtchnl_status_code
+ice_dcf_post_aq_send_cmd(struct ice_pf *pf, struct ice_aq_desc *aq_desc,
+ u8 *aq_buf)
+{
+ enum virtchnl_status_code status = VIRTCHNL_STATUS_SUCCESS;
+ u16 opc = le16_to_cpu(aq_desc->opcode);
+
+ if (!aq_buf)
+ return VIRTCHNL_STATUS_SUCCESS;
+
+ if (opc == ice_aqc_opc_add_sw_rules)
+ status = ice_dcf_handle_add_sw_rule_rsp(pf, aq_buf);
+ else if (opc == ice_aqc_opc_update_sw_rules)
+ status = ice_dcf_handle_updt_sw_rule_rsp(pf, aq_buf);
+ else if (opc == ice_aqc_opc_remove_sw_rules)
+ status = ice_dcf_handle_rm_sw_rule_rsp(pf, aq_buf);
+ else if (opc == ice_aqc_opc_alloc_res)
+ status = ice_dcf_handle_alloc_res_rsp(pf, aq_buf);
+ else if (opc == ice_aqc_opc_free_res)
+ status = ice_dcf_handle_free_res_rsp(pf, aq_buf);
+
+ return status;
+}
+
+/**
+ * ice_dcf_pre_aq_send_cmd - check if it needs to send the command to firmware
+ * @vf: pointer to the VF info
+ * @aq_desc: descriptor describing the command
+ * @aq_buf: the AdminQ command buffer
+ * @aq_buf_size: the AdminQ command buffer size
+ */
+bool
+ice_dcf_pre_aq_send_cmd(struct ice_vf *vf, struct ice_aq_desc *aq_desc,
+ u8 *aq_buf, u16 aq_buf_size)
+{
+ struct ice_pf *pf = vf->pf;
+
+ switch (le16_to_cpu(aq_desc->opcode)) {
+ case ice_aqc_opc_update_sw_rules:
+ {
+ struct ice_dcf_vsi_list_info *vsi_list_info;
+ struct ice_aqc_sw_rules_elem *s_rule;
+ u16 list_id, vsi_id;
+
+ if (aq_buf_size < ICE_SW_RULE_VSI_LIST_SIZE(1))
+ break;
+
+ s_rule = (struct ice_aqc_sw_rules_elem *)aq_buf;
+ if (le16_to_cpu(s_rule->type) !=
+ ICE_AQC_SW_RULES_T_VSI_LIST_CLEAR ||
+ le16_to_cpu(s_rule->pdata.vsi_list.number_vsi) != 1)
+ break;
+
+ list_id = le16_to_cpu(s_rule->pdata.vsi_list.index);
+ vsi_list_info = ice_dcf_find_vsi_list_info(pf, list_id);
+ if (!vsi_list_info)
+ break;
+
+ vsi_id = le16_to_cpu(s_rule->pdata.vsi_list.vsi[0]);
+ if (vsi_id >= ICE_HW_VSI_ID_MAX ||
+ test_bit(vsi_id, vsi_list_info->hw_vsi_map))
+ break;
+
+ /* The VSI is removed from list already, no need to send the
+ * command to firmware.
+ */
+ return true;
+ }
+ case ice_aqc_opc_remove_sw_rules:
+ {
+ struct ice_aqc_sw_rules_elem *s_rule;
+ u16 rule_id;
+
+ if (aq_buf_size < ICE_SW_RULE_RX_TX_NO_HDR_SIZE)
+ break;
+
+ s_rule = (struct ice_aqc_sw_rules_elem *)aq_buf;
+ if (le16_to_cpu(s_rule->type) != ICE_AQC_SW_RULES_T_LKUP_RX)
+ break;
+
+ rule_id = le16_to_cpu(s_rule->pdata.lkup_tx_rx.index);
+ if (ice_dcf_find_sw_rule(pf, rule_id))
+ break;
+
+ /* The switch rule is removed already, no need to send the
+ * command to firmware.
+ */
+ return true;
+ }
+
+ default:
+ break;
+ }
+
+ return false;
+}
@@ -21,10 +21,42 @@ enum ice_dcf_state {
ICE_DCF_STATE_PAUSE,
};
+struct ice_dcf_sw_rule_entry;
+
+#define ICE_HW_VSI_ID_MAX BIT(10) /* The AQ VSI number uses 10 bits */
+
+struct ice_dcf_vsi_list_info {
+ struct list_head list_entry;
+ struct ice_dcf_sw_rule_entry *sw_rule;
+ u16 list_id;
+
+ u16 vsi_count;
+ DECLARE_BITMAP(hw_vsi_map, ICE_HW_VSI_ID_MAX);
+};
+
+struct ice_dcf_sw_rule_entry {
+ struct list_head list_entry;
+ u16 rule_id;
+
+ /* Only support ICE_FWD_TO_VSI and ICE_FWD_TO_VSI_LIST */
+ enum ice_sw_fwd_act_type fltr_act;
+ /* Depending on filter action */
+ union {
+ u16 hw_vsi_id:10;
+ u16 vsi_list_id:10;
+ } fwd_id;
+
+ struct ice_dcf_vsi_list_info *vsi_list_info;
+};
+
struct ice_dcf {
struct ice_vf *vf;
enum ice_dcf_state state;
+ /* Trace the switch rules added/removed by DCF */
+ struct list_head sw_rule_head;
+ struct list_head vsi_list_info_head;
+
/* Handle the AdminQ command between the DCF (Device Config Function)
* and the firmware.
*/
@@ -46,5 +78,14 @@ bool ice_check_dcf_allowed(struct ice_vf *vf);
bool ice_is_vf_dcf(struct ice_vf *vf);
enum ice_dcf_state ice_dcf_get_state(struct ice_pf *pf);
void ice_dcf_set_state(struct ice_pf *pf, enum ice_dcf_state state);
+void ice_dcf_init_sw_rule_mgmt(struct ice_pf *pf);
+void ice_rm_all_dcf_sw_rules(struct ice_pf *pf);
+void ice_rm_dcf_sw_vsi_rule(struct ice_pf *pf, u16 hw_vsi_id);
+bool
+ice_dcf_pre_aq_send_cmd(struct ice_vf *vf, struct ice_aq_desc *aq_desc,
+ u8 *aq_buf, u16 aq_buf_size);
+enum virtchnl_status_code
+ice_dcf_post_aq_send_cmd(struct ice_pf *pf, struct ice_aq_desc *aq_desc,
+ u8 *aq_buf);
#endif /* CONFIG_PCI_IOV */
#endif /* _ICE_DCF_H_ */
@@ -23,24 +23,10 @@
* In case of Ether type filter it is treated as header without VLAN tag
* and byte 12 and 13 is used to program a given Ether type instead
*/
-#define DUMMY_ETH_HDR_LEN 16
static const u8 dummy_eth_header[DUMMY_ETH_HDR_LEN] = { 0x2, 0, 0, 0, 0, 0,
0x2, 0, 0, 0, 0, 0,
0x81, 0, 0, 0};
-#define ICE_SW_RULE_RX_TX_ETH_HDR_SIZE \
- (offsetof(struct ice_aqc_sw_rules_elem, pdata.lkup_tx_rx.hdr) + \
- (DUMMY_ETH_HDR_LEN * \
- sizeof(((struct ice_sw_rule_lkup_rx_tx *)0)->hdr[0])))
-#define ICE_SW_RULE_RX_TX_NO_HDR_SIZE \
- (offsetof(struct ice_aqc_sw_rules_elem, pdata.lkup_tx_rx.hdr))
-#define ICE_SW_RULE_LG_ACT_SIZE(n) \
- (offsetof(struct ice_aqc_sw_rules_elem, pdata.lg_act.act) + \
- ((n) * sizeof(((struct ice_sw_rule_lg_act *)0)->act[0])))
-#define ICE_SW_RULE_VSI_LIST_SIZE(n) \
- (offsetof(struct ice_aqc_sw_rules_elem, pdata.vsi_list.vsi) + \
- ((n) * sizeof(((struct ice_sw_rule_vsi_list *)0)->vsi[0])))
-
/**
* ice_init_def_sw_recp - initialize the recipe book keeping tables
* @hw: pointer to the HW struct
@@ -490,7 +476,7 @@ ice_aq_alloc_free_vsi_list(struct ice_hw *hw, u16 *vsi_list_id,
*
* Add(0x02a0)/Update(0x02a1)/Remove(0x02a2) switch rules commands to firmware
*/
-static enum ice_status
+enum ice_status
ice_aq_sw_rules(struct ice_hw *hw, void *rule_list, u16 rule_list_sz,
u8 num_rules, enum ice_adminq_opc opc, struct ice_sq_cd *cd)
{
@@ -14,6 +14,20 @@
#define ICE_VSI_INVAL_ID 0xffff
#define ICE_INVAL_Q_HANDLE 0xFFFF
+#define DUMMY_ETH_HDR_LEN 16
+#define ICE_SW_RULE_RX_TX_ETH_HDR_SIZE \
+ (offsetof(struct ice_aqc_sw_rules_elem, pdata.lkup_tx_rx.hdr) + \
+ (DUMMY_ETH_HDR_LEN * \
+ sizeof(((struct ice_sw_rule_lkup_rx_tx *)0)->hdr[0])))
+#define ICE_SW_RULE_RX_TX_NO_HDR_SIZE \
+ (offsetof(struct ice_aqc_sw_rules_elem, pdata.lkup_tx_rx.hdr))
+#define ICE_SW_RULE_LG_ACT_SIZE(n) \
+ (offsetof(struct ice_aqc_sw_rules_elem, pdata.lg_act.act) + \
+ ((n) * sizeof(((struct ice_sw_rule_lg_act *)0)->act[0])))
+#define ICE_SW_RULE_VSI_LIST_SIZE(n) \
+ (offsetof(struct ice_aqc_sw_rules_elem, pdata.vsi_list.vsi) + \
+ ((n) * sizeof(((struct ice_sw_rule_vsi_list *)0)->vsi[0])))
+
/* VSI context structure for add/get/update/free operations */
struct ice_vsi_ctx {
u16 vsi_num;
@@ -28,15 +42,6 @@ struct ice_vsi_ctx {
struct ice_q_ctx *lan_q_ctx[ICE_MAX_TRAFFIC_CLASS];
};
-enum ice_sw_fwd_act_type {
- ICE_FWD_TO_VSI = 0,
- ICE_FWD_TO_VSI_LIST, /* Do not use this when adding filter */
- ICE_FWD_TO_Q,
- ICE_FWD_TO_QGRP,
- ICE_DROP_PACKET,
- ICE_INVAL_ACT
-};
-
/* Switch recipe ID enum values are specific to hardware */
enum ice_sw_lkup_type {
ICE_SW_LKUP_ETHERTYPE = 0,
@@ -247,5 +252,7 @@ bool ice_is_vsi_valid(struct ice_hw *hw, u16 vsi_handle);
enum ice_status ice_replay_vsi_all_fltr(struct ice_hw *hw, u16 vsi_handle);
void ice_rm_all_sw_replay_rule_info(struct ice_hw *hw);
-
+enum ice_status
+ice_aq_sw_rules(struct ice_hw *hw, void *rule_list, u16 rule_list_sz,
+ u8 num_rules, enum ice_adminq_opc opc, struct ice_sq_cd *cd);
#endif /* _ICE_SWITCH_H_ */
@@ -707,6 +707,15 @@ struct ice_hw_port_stats {
u64 fd_sb_match;
};
+enum ice_sw_fwd_act_type {
+ ICE_FWD_TO_VSI = 0,
+ ICE_FWD_TO_VSI_LIST, /* Do not use this when adding filter */
+ ICE_FWD_TO_Q,
+ ICE_FWD_TO_QGRP,
+ ICE_DROP_PACKET,
+ ICE_INVAL_ACT
+};
+
/* Checksum and Shadow RAM pointers */
#define ICE_SR_BOOT_CFG_PTR 0x132
#define ICE_NVM_OROM_VER_OFF 0x02
@@ -361,6 +361,7 @@ void ice_free_vfs(struct ice_pf *pf)
dev_warn(dev, "VFs are assigned - not disabling SR-IOV\n");
if (ice_dcf_get_state(pf) != ICE_DCF_STATE_OFF) {
+ ice_rm_all_dcf_sw_rules(pf);
ice_dcf_set_state(pf, ICE_DCF_STATE_OFF);
pf->dcf.vf = NULL;
}
@@ -1055,6 +1056,9 @@ static void ice_vf_clear_counters(struct ice_vf *vf)
*/
static void ice_vf_pre_vsi_rebuild(struct ice_vf *vf)
{
+ /* Remove switch rules associated with the reset VF */
+ ice_rm_dcf_sw_vsi_rule(vf->pf, vf->lan_vsi_num);
+
ice_vf_clear_counters(vf);
ice_clear_vf_reset_trigger(vf);
}
@@ -1584,6 +1588,8 @@ static int ice_ena_vfs(struct ice_pf *pf, u16 num_vfs)
if (ret)
goto err_pci_disable_sriov;
+ ice_dcf_init_sw_rule_mgmt(pf);
+
if (ice_set_per_vf_res(pf)) {
dev_err(dev, "Not enough resources for %d VFs, try with fewer number of VFs\n",
num_vfs);
@@ -2013,6 +2019,13 @@ static int ice_vc_get_vf_res_msg(struct ice_vf *vf, u8 *msg)
ICE_DCF_VFID);
} else if (ice_is_vf_dcf(vf) &&
ice_dcf_get_state(pf) != ICE_DCF_STATE_OFF) {
+ /* If a designated DCF requests AVF functionality from the
+ * same VF without the DCF gracefully relinquishing the DCF
+ * functionality first, remove ALL switch filters that were
+ * added by the DCF.
+ */
+ dev_info(ice_pf_to_dev(pf), "DCF is not in the OFF state, removing all switch filters that were added by the DCF\n");
+ ice_rm_all_dcf_sw_rules(pf);
ice_dcf_set_state(pf, ICE_DCF_STATE_OFF);
pf->dcf.vf = NULL;
ice_reset_vf(vf, false);
@@ -3724,6 +3737,18 @@ ice_dcf_handle_aq_cmd(struct ice_vf *vf, struct ice_aq_desc *aq_desc,
if ((aq_buf && !aq_buf_size) || (!aq_buf && aq_buf_size))
return -EINVAL;
+ if (ice_dcf_pre_aq_send_cmd(vf, aq_desc, aq_buf, aq_buf_size)) {
+ ret = ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_DCF_CMD_DESC,
+ VIRTCHNL_STATUS_SUCCESS,
+ (u8 *)aq_desc, sizeof(*aq_desc));
+ if (ret || !aq_buf_size)
+ return ret;
+
+ v_op = VIRTCHNL_OP_DCF_CMD_BUFF;
+ v_ret = VIRTCHNL_STATUS_SUCCESS;
+ goto err;
+ }
+
aq_ret = ice_aq_send_cmd(&pf->hw, aq_desc, aq_buf, aq_buf_size, NULL);
/* It needs to send back the AQ response message if ICE_ERR_AQ_ERROR
* returns, some AdminQ handlers will use the error code filled by FW
@@ -3735,6 +3760,14 @@ ice_dcf_handle_aq_cmd(struct ice_vf *vf, struct ice_aq_desc *aq_desc,
goto err;
}
+ if (aq_ret != ICE_ERR_AQ_ERROR) {
+ v_ret = ice_dcf_post_aq_send_cmd(pf, aq_desc, aq_buf);
+ if (v_ret != VIRTCHNL_STATUS_SUCCESS) {
+ v_op = VIRTCHNL_OP_DCF_CMD_DESC;
+ goto err;
+ }
+ }
+
ret = ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_DCF_CMD_DESC, v_ret,
(u8 *)aq_desc, sizeof(*aq_desc));
/* Bail out so we don't send the VIRTCHNL_OP_DCF_CMD_BUFF message
@@ -3841,6 +3874,7 @@ static int ice_vc_dis_dcf_cap(struct ice_vf *vf)
if (vf->driver_caps & VIRTCHNL_VF_CAP_DCF) {
vf->driver_caps &= ~VIRTCHNL_VF_CAP_DCF;
+ ice_rm_all_dcf_sw_rules(vf->pf);
ice_dcf_set_state(vf->pf, ICE_DCF_STATE_OFF);
vf->pf->dcf.vf = NULL;
}
@@ -4257,8 +4291,14 @@ int ice_set_vf_trust(struct net_device *netdev, int vf_id, bool trusted)
if (trusted == vf->trusted)
return 0;
+ /* If the trust mode of a given DCF is taken away without the DCF
+ * gracefully relinquishing the DCF functionality, remove ALL switch
+ * filters that were added by the DCF and treat this VF as any other
+ * untrusted AVF.
+ */
if (ice_is_vf_dcf(vf) && !trusted &&
ice_dcf_get_state(pf) != ICE_DCF_STATE_OFF) {
+ ice_rm_all_dcf_sw_rules(pf);
ice_dcf_set_state(pf, ICE_DCF_STATE_OFF);
pf->dcf.vf = NULL;
vf->driver_caps &= ~VIRTCHNL_VF_CAP_DCF;