Message ID | 20200701012557.40234-6-haiyue.wang@intel.com |
---|---|
State | Superseded |
Delegated to: | Anthony Nguyen |
Headers | show |
Series | ice: add Intel DCF mode support | expand |
> -----Original Message----- > From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of > Haiyue Wang > Sent: Tuesday, June 30, 2020 6:26 PM > To: intel-wired-lan@lists.osuosl.org > Cc: Zhang, Xiao <xiao.zhang@intel.com>; Xing, Beilei <beilei.xing@intel.com> > Subject: [Intel-wired-lan] [net-next, v7 5/5] ice: add switch rule management > for DCF > > The PF shall track all the outstanding switch filters (filter IDs to be > precise) added by the DCF. > > Upon a VF reset event, the PF shall clear all outstanding switch filters for the > given VF. Upon completion of either VF or PF reset, the PF shall skip replay of > filters that were added by the DCF. The PF shall however continue to replay > the filters that were not added by DCF for the VF(s). > > If the trust mode of the DCF is taken away without the DCF gracefully > relinquishing the DCF functionality (by way appropriate virtchnl message > exchanges), then the PF shall remove all switch filters that were added by > the DCF. The PF shall transition back from DCF mode to regular mode, the VF > shall be treated as any other untrusted VF, and the PF will reset the VF. > > If a designated DCF requests AVF functionality from the same VF (VF-ID) > without the DCF gracefully relinquishing the DCF functionality first (by way > appropriate virtchnl message exchanges), the PF shall remove all the switch > filters that were added by the DCF. > > Signed-off-by: Xiao Zhang <xiao.zhang@intel.com> > Signed-off-by: Beilei Xing <beilei.xing@intel.com> > Signed-off-by: Haiyue Wang <haiyue.wang@intel.com> > --- > drivers/net/ethernet/intel/ice/ice_dcf.c | 713 ++++++++++++++++++ > drivers/net/ethernet/intel/ice/ice_dcf.h | 41 + > drivers/net/ethernet/intel/ice/ice_switch.c | 16 +- > drivers/net/ethernet/intel/ice/ice_switch.h | 27 +- > drivers/net/ethernet/intel/ice/ice_type.h | 9 + > .../net/ethernet/intel/ice/ice_virtchnl_pf.c | 40 + > 6 files changed, 821 insertions(+), 25 deletions(-) Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
On Wed, 2020-07-01 at 09:25 +0800, Haiyue Wang wrote: > The PF shall track all the outstanding switch filters (filter IDs to > be > precise) added by the DCF. > > Upon a VF reset event, the PF shall clear all outstanding switch > filters > for the given VF. Upon completion of either VF or PF reset, the PF > shall > skip replay of filters that were added by the DCF. The PF shall > however > continue to replay the filters that were not added by DCF for the > VF(s). > > If the trust mode of the DCF is taken away without the DCF gracefully > relinquishing the DCF functionality (by way appropriate virtchnl > message > exchanges), then the PF shall remove all switch filters that were > added > by the DCF. The PF shall transition back from DCF mode to regular > mode, > the VF shall be treated as any other untrusted VF, and the PF will > reset > the VF. > > If a designated DCF requests AVF functionality from the same VF (VF- > ID) > without the DCF gracefully relinquishing the DCF functionality first > (by > way appropriate virtchnl message exchanges), the PF shall remove all > the > switch filters that were added by the DCF. > > Signed-off-by: Xiao Zhang <xiao.zhang@intel.com> > Signed-off-by: Beilei Xing <beilei.xing@intel.com> > Signed-off-by: Haiyue Wang <haiyue.wang@intel.com> > --- > drivers/net/ethernet/intel/ice/ice_dcf.c | 713 > ++++++++++++++++++ > drivers/net/ethernet/intel/ice/ice_dcf.h | 41 + > drivers/net/ethernet/intel/ice/ice_switch.c | 16 +- > drivers/net/ethernet/intel/ice/ice_switch.h | 27 +- > drivers/net/ethernet/intel/ice/ice_type.h | 9 + > .../net/ethernet/intel/ice/ice_virtchnl_pf.c | 40 + > 6 files changed, 821 insertions(+), 25 deletions(-) > > diff --git a/drivers/net/ethernet/intel/ice/ice_dcf.c > b/drivers/net/ethernet/intel/ice/ice_dcf.c > index e7d37735aaa5..154005f1b634 100644 > --- a/drivers/net/ethernet/intel/ice/ice_dcf.c > +++ b/drivers/net/ethernet/intel/ice/ice_dcf.c > @@ -124,3 +124,716 @@ 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 mngt > 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_sw_rule_vsi_list > *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- > >index)); > + if (!vsi_list_info) > + return VIRTCHNL_STATUS_ERR_PARAM; > + > + for (i = 0; i < le16_to_cpu(vsi_list->number_vsi); i++) > + ice_dcf_add_vsi_id(vsi_list_info, > + le16_to_cpu(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_sw_rule_vsi_list *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- > >index)); > + if (!vsi_list_info) > + return VIRTCHNL_STATUS_ERR_PARAM; > + > + for (i = 0; i < le16_to_cpu(vsi_list->number_vsi); i++) > + ice_dcf_del_vsi_id(vsi_list_info, > + le16_to_cpu(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_sw_rule_lkup_rx_tx *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->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->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_sw_rule_lkup_rx_tx *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->index); > + sw_rule = ice_dcf_find_sw_rule(pf, rule_id); > + if (!sw_rule) > + return VIRTCHNL_STATUS_ERR_PARAM; > + > + act = le32_to_cpu(lkup->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_sw_rule_lkup_rx_tx *lkup) > +{ > + struct ice_dcf_sw_rule_entry *sw_rule, *tmp; > + u16 rule_id = le16_to_cpu(lkup->index); > + > + 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->pdata.vsi_list); > + else if (type == ICE_AQC_SW_RULES_T_LKUP_RX) > + status = ice_dcf_parse_add_sw_rule_data(pf, > + &em- > >pdata.lkup_tx_rx); > + > + 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->pdata.vsi_list); > + else if (type == ICE_AQC_SW_RULES_T_VSI_LIST_CLEAR) > + status = ice_dcf_clear_vsi_list(pf, &em- > >pdata.vsi_list); > + else if (type == ICE_AQC_SW_RULES_T_LKUP_RX) > + status = ice_dcf_parse_updt_sw_rule_data(pf, > + &em- > >pdata.lkup_tx_rx); > + > + 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- > >pdata.lkup_tx_rx); > + > + 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_CLE > AR || > + 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; > +} > diff --git a/drivers/net/ethernet/intel/ice/ice_dcf.h > b/drivers/net/ethernet/intel/ice/ice_dcf.h > index 1ca228f89a19..23842db0a884 100644 > --- a/drivers/net/ethernet/intel/ice/ice_dcf.h > +++ b/drivers/net/ethernet/intel/ice/ice_dcf.h > @@ -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_ */ > diff --git a/drivers/net/ethernet/intel/ice/ice_switch.c > b/drivers/net/ethernet/intel/ice/ice_switch.c > index ccbe1cc64295..ee434b8d794d 100644 > --- a/drivers/net/ethernet/intel/ice/ice_switch.c > +++ b/drivers/net/ethernet/intel/ice/ice_switch.c > @@ -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) > { Hi Dave, Jakub, This feature is only built when CONFIG_PCI_IOV is set. We end up with this namespace issue using defconfig when checked against namespace.pl since CONFIG_PCI_IOV is not enabled. Externally defined symbols with no external references ice_switch.o ice_aq_sw_rules From a previous patch, neither of you liked the use of CONFIG_ to control static-ness. I wanted to check that you are ok with the namespace issue or if you have a preferred method to resolve this issue. I appreciate your feedback. Thanks, Tony
On Wed, 8 Jul 2020 22:55:21 +0000 Nguyen, Anthony L wrote: > > @@ -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) > > { > > Hi Dave, Jakub, > > This feature is only built when CONFIG_PCI_IOV is set. We end up with > this namespace issue using defconfig when checked against namespace.pl > since CONFIG_PCI_IOV is not enabled. > Externally defined symbols with no external references > ice_switch.o > ice_aq_sw_rules > > From a previous patch, neither of you liked the use of CONFIG_ to > control static-ness. I wanted to check that you are ok with the > namespace issue or if you have a preferred method to resolve this > issue. I appreciate your feedback. IMHO that should be fine. I'd only trust namespace.pl on a all*config kernel, if at all.
diff --git a/drivers/net/ethernet/intel/ice/ice_dcf.c b/drivers/net/ethernet/intel/ice/ice_dcf.c index e7d37735aaa5..154005f1b634 100644 --- a/drivers/net/ethernet/intel/ice/ice_dcf.c +++ b/drivers/net/ethernet/intel/ice/ice_dcf.c @@ -124,3 +124,716 @@ 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 mngt 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_sw_rule_vsi_list *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->index)); + if (!vsi_list_info) + return VIRTCHNL_STATUS_ERR_PARAM; + + for (i = 0; i < le16_to_cpu(vsi_list->number_vsi); i++) + ice_dcf_add_vsi_id(vsi_list_info, + le16_to_cpu(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_sw_rule_vsi_list *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->index)); + if (!vsi_list_info) + return VIRTCHNL_STATUS_ERR_PARAM; + + for (i = 0; i < le16_to_cpu(vsi_list->number_vsi); i++) + ice_dcf_del_vsi_id(vsi_list_info, + le16_to_cpu(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_sw_rule_lkup_rx_tx *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->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->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_sw_rule_lkup_rx_tx *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->index); + sw_rule = ice_dcf_find_sw_rule(pf, rule_id); + if (!sw_rule) + return VIRTCHNL_STATUS_ERR_PARAM; + + act = le32_to_cpu(lkup->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_sw_rule_lkup_rx_tx *lkup) +{ + struct ice_dcf_sw_rule_entry *sw_rule, *tmp; + u16 rule_id = le16_to_cpu(lkup->index); + + 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->pdata.vsi_list); + else if (type == ICE_AQC_SW_RULES_T_LKUP_RX) + status = ice_dcf_parse_add_sw_rule_data(pf, + &em->pdata.lkup_tx_rx); + + 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->pdata.vsi_list); + else if (type == ICE_AQC_SW_RULES_T_VSI_LIST_CLEAR) + status = ice_dcf_clear_vsi_list(pf, &em->pdata.vsi_list); + else if (type == ICE_AQC_SW_RULES_T_LKUP_RX) + status = ice_dcf_parse_updt_sw_rule_data(pf, + &em->pdata.lkup_tx_rx); + + 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->pdata.lkup_tx_rx); + + 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; +} diff --git a/drivers/net/ethernet/intel/ice/ice_dcf.h b/drivers/net/ethernet/intel/ice/ice_dcf.h index 1ca228f89a19..23842db0a884 100644 --- a/drivers/net/ethernet/intel/ice/ice_dcf.h +++ b/drivers/net/ethernet/intel/ice/ice_dcf.h @@ -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_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_switch.c b/drivers/net/ethernet/intel/ice/ice_switch.c index ccbe1cc64295..ee434b8d794d 100644 --- a/drivers/net/ethernet/intel/ice/ice_switch.c +++ b/drivers/net/ethernet/intel/ice/ice_switch.c @@ -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) { diff --git a/drivers/net/ethernet/intel/ice/ice_switch.h b/drivers/net/ethernet/intel/ice/ice_switch.h index 8b4f9d35c860..05cd6ea24d44 100644 --- a/drivers/net/ethernet/intel/ice/ice_switch.h +++ b/drivers/net/ethernet/intel/ice/ice_switch.h @@ -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_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h index c1ad8622e65c..c2eff68d5469 100644 --- a/drivers/net/ethernet/intel/ice/ice_type.h +++ b/drivers/net/ethernet/intel/ice/ice_type.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 diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl_pf.c b/drivers/net/ethernet/intel/ice/ice_virtchnl_pf.c index 919f1cec784e..fa1f84118456 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl_pf.c @@ -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); @@ -3723,6 +3736,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 @@ -3734,6 +3759,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 @@ -3840,6 +3873,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; } @@ -4256,8 +4290,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;