// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2022, Intel Corporation. */ #include "rss.h" #include "ice_vf_lib_private.h" #include "ice.h" #define FIELD_SELECTOR(proto_hdr_field) \ BIT((proto_hdr_field) & PROTO_HDR_FIELD_MASK) struct ice_vc_hdr_match_type { u32 vc_hdr; /* virtchnl headers (VIRTCHNL_PROTO_HDR_XXX) */ u32 ice_hdr; /* ice headers (ICE_FLOW_SEG_HDR_XXX) */ }; static const struct ice_vc_hdr_match_type ice_vc_hdr_list[] = { {VIRTCHNL_PROTO_HDR_NONE, ICE_FLOW_SEG_HDR_NONE}, {VIRTCHNL_PROTO_HDR_ETH, ICE_FLOW_SEG_HDR_ETH}, {VIRTCHNL_PROTO_HDR_S_VLAN, ICE_FLOW_SEG_HDR_VLAN}, {VIRTCHNL_PROTO_HDR_C_VLAN, ICE_FLOW_SEG_HDR_VLAN}, {VIRTCHNL_PROTO_HDR_IPV4, ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV_OTHER}, {VIRTCHNL_PROTO_HDR_IPV6, ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_IPV_OTHER}, {VIRTCHNL_PROTO_HDR_TCP, ICE_FLOW_SEG_HDR_TCP}, {VIRTCHNL_PROTO_HDR_UDP, ICE_FLOW_SEG_HDR_UDP}, {VIRTCHNL_PROTO_HDR_SCTP, ICE_FLOW_SEG_HDR_SCTP}, {VIRTCHNL_PROTO_HDR_PPPOE, ICE_FLOW_SEG_HDR_PPPOE}, {VIRTCHNL_PROTO_HDR_GTPU_IP, ICE_FLOW_SEG_HDR_GTPU_IP}, {VIRTCHNL_PROTO_HDR_GTPU_EH, ICE_FLOW_SEG_HDR_GTPU_EH}, {VIRTCHNL_PROTO_HDR_GTPU_EH_PDU_DWN, ICE_FLOW_SEG_HDR_GTPU_DWN}, {VIRTCHNL_PROTO_HDR_GTPU_EH_PDU_UP, ICE_FLOW_SEG_HDR_GTPU_UP}, {VIRTCHNL_PROTO_HDR_L2TPV3, ICE_FLOW_SEG_HDR_L2TPV3}, {VIRTCHNL_PROTO_HDR_ESP, ICE_FLOW_SEG_HDR_ESP}, {VIRTCHNL_PROTO_HDR_AH, ICE_FLOW_SEG_HDR_AH}, {VIRTCHNL_PROTO_HDR_PFCP, ICE_FLOW_SEG_HDR_PFCP_SESSION}, }; struct ice_vc_hash_field_match_type { u32 vc_hdr; /* virtchnl headers * (VIRTCHNL_PROTO_HDR_XXX) */ u32 vc_hash_field; /* virtchnl hash fields selector * FIELD_SELECTOR((VIRTCHNL_PROTO_HDR_ETH_XXX)) */ u64 ice_hash_field; /* ice hash fields * (BIT_ULL(ICE_FLOW_FIELD_IDX_XXX)) */ }; static const struct ice_vc_hash_field_match_type ice_vc_hash_field_list[] = { {VIRTCHNL_PROTO_HDR_ETH, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_ETH_SRC), BIT_ULL(ICE_FLOW_FIELD_IDX_ETH_SA)}, {VIRTCHNL_PROTO_HDR_ETH, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_ETH_DST), BIT_ULL(ICE_FLOW_FIELD_IDX_ETH_DA)}, {VIRTCHNL_PROTO_HDR_ETH, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_ETH_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_ETH_DST), ICE_FLOW_HASH_ETH}, {VIRTCHNL_PROTO_HDR_ETH, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_ETH_ETHERTYPE), BIT_ULL(ICE_FLOW_FIELD_IDX_ETH_TYPE)}, {VIRTCHNL_PROTO_HDR_S_VLAN, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_S_VLAN_ID), BIT_ULL(ICE_FLOW_FIELD_IDX_S_VLAN)}, {VIRTCHNL_PROTO_HDR_C_VLAN, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_C_VLAN_ID), BIT_ULL(ICE_FLOW_FIELD_IDX_C_VLAN)}, {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_SA)}, {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_DA)}, {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST), ICE_FLOW_HASH_IPV4}, {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_SA) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT)}, {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_DA) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT)}, {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT), ICE_FLOW_HASH_IPV4 | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT)}, {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT)}, {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_SRC), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_SA)}, {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_DST), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_DA)}, {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_DST), ICE_FLOW_HASH_IPV6}, {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PROT), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_SA) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PROT)}, {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_DST) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PROT), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_DA) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PROT)}, {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_DST) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PROT), ICE_FLOW_HASH_IPV6 | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PROT)}, {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PROT), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PROT)}, {VIRTCHNL_PROTO_HDR_TCP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_SRC_PORT), BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_SRC_PORT)}, {VIRTCHNL_PROTO_HDR_TCP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_DST_PORT), BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_DST_PORT)}, {VIRTCHNL_PROTO_HDR_TCP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_SRC_PORT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_DST_PORT), ICE_FLOW_HASH_TCP_PORT}, {VIRTCHNL_PROTO_HDR_UDP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_SRC_PORT), BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_SRC_PORT)}, {VIRTCHNL_PROTO_HDR_UDP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_DST_PORT), BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_DST_PORT)}, {VIRTCHNL_PROTO_HDR_UDP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_SRC_PORT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_DST_PORT), ICE_FLOW_HASH_UDP_PORT}, {VIRTCHNL_PROTO_HDR_SCTP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_SRC_PORT), BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT)}, {VIRTCHNL_PROTO_HDR_SCTP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_DST_PORT), BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_DST_PORT)}, {VIRTCHNL_PROTO_HDR_SCTP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_SRC_PORT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_DST_PORT), ICE_FLOW_HASH_SCTP_PORT}, {VIRTCHNL_PROTO_HDR_PPPOE, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_PPPOE_SESS_ID), BIT_ULL(ICE_FLOW_FIELD_IDX_PPPOE_SESS_ID)}, {VIRTCHNL_PROTO_HDR_GTPU_IP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_GTPU_IP_TEID), BIT_ULL(ICE_FLOW_FIELD_IDX_GTPU_IP_TEID)}, {VIRTCHNL_PROTO_HDR_L2TPV3, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_L2TPV3_SESS_ID), BIT_ULL(ICE_FLOW_FIELD_IDX_L2TPV3_SESS_ID)}, {VIRTCHNL_PROTO_HDR_ESP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_ESP_SPI), BIT_ULL(ICE_FLOW_FIELD_IDX_ESP_SPI)}, {VIRTCHNL_PROTO_HDR_AH, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_AH_SPI), BIT_ULL(ICE_FLOW_FIELD_IDX_AH_SPI)}, {VIRTCHNL_PROTO_HDR_PFCP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_PFCP_SEID), BIT_ULL(ICE_FLOW_FIELD_IDX_PFCP_SEID)}, }; /** * ice_vc_validate_pattern * @vf: pointer to the VF info * @proto: virtchnl protocol headers * * validate the pattern is supported or not. * * Return: true on success, false on error. */ bool ice_vc_validate_pattern(struct ice_vf *vf, struct virtchnl_proto_hdrs *proto) { bool is_ipv4 = false; bool is_ipv6 = false; bool is_udp = false; u16 ptype = -1; int i = 0; while (i < proto->count && proto->proto_hdr[i].type != VIRTCHNL_PROTO_HDR_NONE) { switch (proto->proto_hdr[i].type) { case VIRTCHNL_PROTO_HDR_ETH: ptype = ICE_PTYPE_MAC_PAY; break; case VIRTCHNL_PROTO_HDR_IPV4: ptype = ICE_PTYPE_IPV4_PAY; is_ipv4 = true; break; case VIRTCHNL_PROTO_HDR_IPV6: ptype = ICE_PTYPE_IPV6_PAY; is_ipv6 = true; break; case VIRTCHNL_PROTO_HDR_UDP: if (is_ipv4) ptype = ICE_PTYPE_IPV4_UDP_PAY; else if (is_ipv6) ptype = ICE_PTYPE_IPV6_UDP_PAY; is_udp = true; break; case VIRTCHNL_PROTO_HDR_TCP: if (is_ipv4) ptype = ICE_PTYPE_IPV4_TCP_PAY; else if (is_ipv6) ptype = ICE_PTYPE_IPV6_TCP_PAY; break; case VIRTCHNL_PROTO_HDR_SCTP: if (is_ipv4) ptype = ICE_PTYPE_IPV4_SCTP_PAY; else if (is_ipv6) ptype = ICE_PTYPE_IPV6_SCTP_PAY; break; case VIRTCHNL_PROTO_HDR_GTPU_IP: case VIRTCHNL_PROTO_HDR_GTPU_EH: if (is_ipv4) ptype = ICE_MAC_IPV4_GTPU; else if (is_ipv6) ptype = ICE_MAC_IPV6_GTPU; goto out; case VIRTCHNL_PROTO_HDR_L2TPV3: if (is_ipv4) ptype = ICE_MAC_IPV4_L2TPV3; else if (is_ipv6) ptype = ICE_MAC_IPV6_L2TPV3; goto out; case VIRTCHNL_PROTO_HDR_ESP: if (is_ipv4) ptype = is_udp ? ICE_MAC_IPV4_NAT_T_ESP : ICE_MAC_IPV4_ESP; else if (is_ipv6) ptype = is_udp ? ICE_MAC_IPV6_NAT_T_ESP : ICE_MAC_IPV6_ESP; goto out; case VIRTCHNL_PROTO_HDR_AH: if (is_ipv4) ptype = ICE_MAC_IPV4_AH; else if (is_ipv6) ptype = ICE_MAC_IPV6_AH; goto out; case VIRTCHNL_PROTO_HDR_PFCP: if (is_ipv4) ptype = ICE_MAC_IPV4_PFCP_SESSION; else if (is_ipv6) ptype = ICE_MAC_IPV6_PFCP_SESSION; goto out; default: break; } i++; } out: return ice_hw_ptype_ena(&vf->pf->hw, ptype); } /** * ice_vc_parse_rss_cfg - parses hash fields and headers from * a specific virtchnl RSS cfg * @hw: pointer to the hardware * @rss_cfg: pointer to the virtchnl RSS cfg * @hash_cfg: pointer to the HW hash configuration * * Return true if all the protocol header and hash fields in the RSS cfg could * be parsed, else return false * * This function parses the virtchnl RSS cfg to be the intended * hash fields and the intended header for RSS configuration */ static bool ice_vc_parse_rss_cfg(struct ice_hw *hw, struct virtchnl_rss_cfg *rss_cfg, struct ice_rss_hash_cfg *hash_cfg) { const struct ice_vc_hash_field_match_type *hf_list; const struct ice_vc_hdr_match_type *hdr_list; int i, hf_list_len, hdr_list_len; u32 *addl_hdrs = &hash_cfg->addl_hdrs; u64 *hash_flds = &hash_cfg->hash_flds; /* set outer layer RSS as default */ hash_cfg->hdr_type = ICE_RSS_OUTER_HEADERS; if (rss_cfg->rss_algorithm == VIRTCHNL_RSS_ALG_TOEPLITZ_SYMMETRIC) hash_cfg->symm = true; else hash_cfg->symm = false; hf_list = ice_vc_hash_field_list; hf_list_len = ARRAY_SIZE(ice_vc_hash_field_list); hdr_list = ice_vc_hdr_list; hdr_list_len = ARRAY_SIZE(ice_vc_hdr_list); for (i = 0; i < rss_cfg->proto_hdrs.count; i++) { struct virtchnl_proto_hdr *proto_hdr = &rss_cfg->proto_hdrs.proto_hdr[i]; bool hdr_found = false; int j; /* Find matched ice headers according to virtchnl headers. */ for (j = 0; j < hdr_list_len; j++) { struct ice_vc_hdr_match_type hdr_map = hdr_list[j]; if (proto_hdr->type == hdr_map.vc_hdr) { *addl_hdrs |= hdr_map.ice_hdr; hdr_found = true; } } if (!hdr_found) return false; /* Find matched ice hash fields according to * virtchnl hash fields. */ for (j = 0; j < hf_list_len; j++) { struct ice_vc_hash_field_match_type hf_map = hf_list[j]; if (proto_hdr->type == hf_map.vc_hdr && proto_hdr->field_selector == hf_map.vc_hash_field) { *hash_flds |= hf_map.ice_hash_field; break; } } } return true; } /** * ice_vf_adv_rss_offload_ena - determine if capabilities support advanced * RSS offloads * @caps: VF driver negotiated capabilities * * Return true if VIRTCHNL_VF_OFFLOAD_ADV_RSS_PF capability is set, * else return false */ static bool ice_vf_adv_rss_offload_ena(u32 caps) { return !!(caps & VIRTCHNL_VF_OFFLOAD_ADV_RSS_PF); } /** * ice_vc_handle_rss_cfg * @vf: pointer to the VF info * @msg: pointer to the message buffer * @add: add a RSS config if true, otherwise delete a RSS config * * This function adds/deletes a RSS config */ int ice_vc_handle_rss_cfg(struct ice_vf *vf, u8 *msg, bool add) { u32 v_opcode = add ? VIRTCHNL_OP_ADD_RSS_CFG : VIRTCHNL_OP_DEL_RSS_CFG; struct virtchnl_rss_cfg *rss_cfg = (struct virtchnl_rss_cfg *)msg; enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS; struct device *dev = ice_pf_to_dev(vf->pf); struct ice_hw *hw = &vf->pf->hw; struct ice_vsi *vsi; if (!test_bit(ICE_FLAG_RSS_ENA, vf->pf->flags)) { dev_dbg(dev, "VF %d attempting to configure RSS, but RSS is not supported by the PF\n", vf->vf_id); v_ret = VIRTCHNL_STATUS_ERR_NOT_SUPPORTED; goto error_param; } if (!ice_vf_adv_rss_offload_ena(vf->driver_caps)) { dev_dbg(dev, "VF %d attempting to configure RSS, but Advanced RSS offload is not supported\n", vf->vf_id); v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } if (rss_cfg->proto_hdrs.count > VIRTCHNL_MAX_NUM_PROTO_HDRS || rss_cfg->rss_algorithm < VIRTCHNL_RSS_ALG_TOEPLITZ_ASYMMETRIC || rss_cfg->rss_algorithm > VIRTCHNL_RSS_ALG_XOR_SYMMETRIC) { dev_dbg(dev, "VF %d attempting to configure RSS, but RSS configuration is not valid\n", vf->vf_id); v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } vsi = ice_get_vf_vsi(vf); if (!vsi) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } if (!ice_vc_validate_pattern(vf, &rss_cfg->proto_hdrs)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } if (rss_cfg->rss_algorithm == VIRTCHNL_RSS_ALG_R_ASYMMETRIC) { struct ice_vsi_ctx *ctx; u8 lut_type, hash_type; int status; lut_type = ICE_AQ_VSI_Q_OPT_RSS_LUT_VSI; hash_type = add ? ICE_AQ_VSI_Q_OPT_RSS_HASH_XOR : ICE_AQ_VSI_Q_OPT_RSS_HASH_TPLZ; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) { v_ret = VIRTCHNL_STATUS_ERR_NO_MEMORY; goto error_param; } ctx->info.q_opt_rss = FIELD_PREP(ICE_AQ_VSI_Q_OPT_RSS_LUT_M, lut_type) | FIELD_PREP(ICE_AQ_VSI_Q_OPT_RSS_HASH_M, hash_type); /* Preserve existing queueing option setting */ ctx->info.q_opt_rss |= (vsi->info.q_opt_rss & ICE_AQ_VSI_Q_OPT_RSS_GBL_LUT_M); ctx->info.q_opt_tc = vsi->info.q_opt_tc; ctx->info.q_opt_flags = vsi->info.q_opt_rss; ctx->info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_Q_OPT_VALID); status = ice_update_vsi(hw, vsi->idx, ctx, NULL); if (status) { dev_err(dev, "update VSI for RSS failed, err %d aq_err %s\n", status, libie_aq_str(hw->adminq.sq_last_status)); v_ret = VIRTCHNL_STATUS_ERR_PARAM; } else { vsi->info.q_opt_rss = ctx->info.q_opt_rss; } kfree(ctx); } else { struct ice_rss_hash_cfg cfg; /* Only check for none raw pattern case */ if (!ice_vc_validate_pattern(vf, &rss_cfg->proto_hdrs)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } cfg.addl_hdrs = ICE_FLOW_SEG_HDR_NONE; cfg.hash_flds = ICE_HASH_INVALID; cfg.hdr_type = ICE_RSS_ANY_HEADERS; if (!ice_vc_parse_rss_cfg(hw, rss_cfg, &cfg)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } if (add) { if (ice_add_rss_cfg(hw, vsi, &cfg)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; dev_err(dev, "ice_add_rss_cfg failed for vsi = %d, v_ret = %d\n", vsi->vsi_num, v_ret); } } else { int status; status = ice_rem_rss_cfg(hw, vsi->idx, &cfg); /* We just ignore -ENOENT, because if two configurations * share the same profile remove one of them actually * removes both, since the profile is deleted. */ if (status && status != -ENOENT) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; dev_err(dev, "ice_rem_rss_cfg failed for VF ID:%d, error:%d\n", vf->vf_id, status); } } } error_param: return ice_vc_send_msg_to_vf(vf, v_opcode, v_ret, NULL, 0); } /** * ice_vc_config_rss_key * @vf: pointer to the VF info * @msg: pointer to the msg buffer * * Configure the VF's RSS key */ int ice_vc_config_rss_key(struct ice_vf *vf, u8 *msg) { enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS; struct virtchnl_rss_key *vrk = (struct virtchnl_rss_key *)msg; struct ice_vsi *vsi; if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } if (!ice_vc_isvalid_vsi_id(vf, vrk->vsi_id)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } if (vrk->key_len != ICE_VSIQF_HKEY_ARRAY_SIZE) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } if (!test_bit(ICE_FLAG_RSS_ENA, vf->pf->flags)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } vsi = ice_get_vf_vsi(vf); if (!vsi) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } if (ice_set_rss_key(vsi, vrk->key)) v_ret = VIRTCHNL_STATUS_ERR_ADMIN_QUEUE_ERROR; error_param: return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_CONFIG_RSS_KEY, v_ret, NULL, 0); } /** * ice_vc_config_rss_lut * @vf: pointer to the VF info * @msg: pointer to the msg buffer * * Configure the VF's RSS LUT */ int ice_vc_config_rss_lut(struct ice_vf *vf, u8 *msg) { struct virtchnl_rss_lut *vrl = (struct virtchnl_rss_lut *)msg; enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS; struct ice_vsi *vsi; if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } if (!ice_vc_isvalid_vsi_id(vf, vrl->vsi_id)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } if (vrl->lut_entries != ICE_LUT_VSI_SIZE) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } if (!test_bit(ICE_FLAG_RSS_ENA, vf->pf->flags)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } vsi = ice_get_vf_vsi(vf); if (!vsi) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } if (ice_set_rss_lut(vsi, vrl->lut, ICE_LUT_VSI_SIZE)) v_ret = VIRTCHNL_STATUS_ERR_ADMIN_QUEUE_ERROR; error_param: return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_CONFIG_RSS_LUT, v_ret, NULL, 0); } /** * ice_vc_config_rss_hfunc * @vf: pointer to the VF info * @msg: pointer to the msg buffer * * Configure the VF's RSS Hash function */ int ice_vc_config_rss_hfunc(struct ice_vf *vf, u8 *msg) { struct virtchnl_rss_hfunc *vrh = (struct virtchnl_rss_hfunc *)msg; enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS; u8 hfunc = ICE_AQ_VSI_Q_OPT_RSS_HASH_TPLZ; struct ice_vsi *vsi; if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } if (!ice_vc_isvalid_vsi_id(vf, vrh->vsi_id)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } if (!test_bit(ICE_FLAG_RSS_ENA, vf->pf->flags)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } vsi = ice_get_vf_vsi(vf); if (!vsi) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } if (vrh->rss_algorithm == VIRTCHNL_RSS_ALG_TOEPLITZ_SYMMETRIC) hfunc = ICE_AQ_VSI_Q_OPT_RSS_HASH_SYM_TPLZ; if (ice_set_rss_hfunc(vsi, hfunc)) v_ret = VIRTCHNL_STATUS_ERR_ADMIN_QUEUE_ERROR; error_param: return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_CONFIG_RSS_HFUNC, v_ret, NULL, 0); } /** * ice_vc_get_rss_hashcfg - return the RSS Hash configuration * @vf: pointer to the VF info */ int ice_vc_get_rss_hashcfg(struct ice_vf *vf) { enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS; struct virtchnl_rss_hashcfg *vrh = NULL; int len = 0, ret; if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto err; } if (!test_bit(ICE_FLAG_RSS_ENA, vf->pf->flags)) { dev_err(ice_pf_to_dev(vf->pf), "RSS not supported by PF\n"); v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto err; } len = sizeof(struct virtchnl_rss_hashcfg); vrh = kzalloc(len, GFP_KERNEL); if (!vrh) { v_ret = VIRTCHNL_STATUS_ERR_NO_MEMORY; len = 0; goto err; } vrh->hashcfg = ICE_DEFAULT_RSS_HASHCFG; err: /* send the response back to the VF */ ret = ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_GET_RSS_HASHCFG_CAPS, v_ret, (u8 *)vrh, len); kfree(vrh); return ret; } /** * ice_vc_set_rss_hashcfg - set RSS Hash configuration bits for the VF * @vf: pointer to the VF info * @msg: pointer to the msg buffer */ int ice_vc_set_rss_hashcfg(struct ice_vf *vf, u8 *msg) { struct virtchnl_rss_hashcfg *vrh = (struct virtchnl_rss_hashcfg *)msg; enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS; struct ice_pf *pf = vf->pf; struct ice_vsi *vsi; struct device *dev; int status; dev = ice_pf_to_dev(pf); if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto err; } if (!test_bit(ICE_FLAG_RSS_ENA, pf->flags)) { dev_err(dev, "RSS not supported by PF\n"); v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto err; } vsi = ice_get_vf_vsi(vf); if (!vsi) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto err; } /* clear all previously programmed RSS configuration to allow VF drivers * the ability to customize the RSS configuration and/or completely * disable RSS */ status = ice_rem_vsi_rss_cfg(&pf->hw, vsi->idx); if (status && !vrh->hashcfg) { /* only report failure to clear the current RSS configuration if * that was clearly the VF's intention (i.e. vrh->hashcfg = 0) */ v_ret = ice_err_to_virt_err(status); goto err; } else if (status) { /* allow the VF to update the RSS configuration even on failure * to clear the current RSS confguration in an attempt to keep * RSS in a working state */ dev_warn(dev, "Failed to clear the RSS configuration for VF %u\n", vf->vf_id); } if (vrh->hashcfg) { status = ice_add_avf_rss_cfg(&pf->hw, vsi, vrh->hashcfg); v_ret = ice_err_to_virt_err(status); } /* save the requested VF configuration */ if (!v_ret) vf->rss_hashcfg = vrh->hashcfg; /* send the response to the VF */ err: return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_SET_RSS_HASHCFG, v_ret, NULL, 0); }