// 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}, {VIRTCHNL_PROTO_HDR_GTPC, ICE_FLOW_SEG_HDR_GTPC}, {VIRTCHNL_PROTO_HDR_L2TPV2, ICE_FLOW_SEG_HDR_L2TPV2}, {VIRTCHNL_PROTO_HDR_IPV4_FRAG, ICE_FLOW_SEG_HDR_IPV_FRAG}, {VIRTCHNL_PROTO_HDR_IPV6_EH_FRAG, ICE_FLOW_SEG_HDR_IPV_FRAG}, {VIRTCHNL_PROTO_HDR_GRE, ICE_FLOW_SEG_HDR_GRE}, }; 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_FRAG_PKID), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_ID)}, {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)}, {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_SA) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)}, {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_DA) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)}, {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM), ICE_FLOW_HASH_IPV4 | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)}, {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_SA) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)}, {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_DA) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)}, {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) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM), ICE_FLOW_HASH_IPV4 | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)}, {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)}, {VIRTCHNL_PROTO_HDR_IPV4_FRAG, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT)}, {VIRTCHNL_PROTO_HDR_IPV4_FRAG, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_SA)}, {VIRTCHNL_PROTO_HDR_IPV4_FRAG, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_DA)}, {VIRTCHNL_PROTO_HDR_IPV4_FRAG, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST), ICE_FLOW_HASH_IPV4}, {VIRTCHNL_PROTO_HDR_IPV4_FRAG, 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_FRAG, 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_FRAG, 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_FRAG, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT)}, {VIRTCHNL_PROTO_HDR_IPV4_FRAG, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_FRAG_PKID), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_ID)}, {VIRTCHNL_PROTO_HDR_IPV4_FRAG, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)}, {VIRTCHNL_PROTO_HDR_IPV4_FRAG, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_SA) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)}, {VIRTCHNL_PROTO_HDR_IPV4_FRAG, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_DA) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)}, {VIRTCHNL_PROTO_HDR_IPV4_FRAG, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM), ICE_FLOW_HASH_IPV4 | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)}, {VIRTCHNL_PROTO_HDR_IPV4_FRAG, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_SA) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)}, {VIRTCHNL_PROTO_HDR_IPV4_FRAG, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_DA) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)}, {VIRTCHNL_PROTO_HDR_IPV4_FRAG, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM), ICE_FLOW_HASH_IPV4 | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)}, {VIRTCHNL_PROTO_HDR_IPV4_FRAG, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)}, {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_IPV6_EH_FRAG, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_EH_FRAG_PKID), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_ID)}, {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_DST), ICE_FLOW_HASH_IPV6_PRE64}, {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_SRC), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PRE64_SA)}, {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_DST), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PRE64_DA)}, {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_DST) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PROT), ICE_FLOW_HASH_IPV6_PRE64 | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PROT)}, {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_SRC) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PROT), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PRE64_SA) | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PROT)}, {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_DST) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PROT), BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PRE64_DA) | 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_TCP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_CHKSUM)}, {VIRTCHNL_PROTO_HDR_TCP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_SRC_PORT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_SRC_PORT) | BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_CHKSUM)}, {VIRTCHNL_PROTO_HDR_TCP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_DST_PORT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_DST_PORT) | BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_CHKSUM)}, {VIRTCHNL_PROTO_HDR_TCP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_SRC_PORT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_DST_PORT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_CHKSUM), ICE_FLOW_HASH_TCP_PORT | BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_CHKSUM)}, {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_UDP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_CHKSUM)}, {VIRTCHNL_PROTO_HDR_UDP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_SRC_PORT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_SRC_PORT) | BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_CHKSUM)}, {VIRTCHNL_PROTO_HDR_UDP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_DST_PORT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_DST_PORT) | BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_CHKSUM)}, {VIRTCHNL_PROTO_HDR_UDP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_SRC_PORT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_DST_PORT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_CHKSUM), ICE_FLOW_HASH_UDP_PORT | BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_CHKSUM)}, {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_SCTP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_CHKSUM)}, {VIRTCHNL_PROTO_HDR_SCTP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_SRC_PORT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT) | BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_CHKSUM)}, {VIRTCHNL_PROTO_HDR_SCTP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_DST_PORT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_CHKSUM), BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_DST_PORT) | BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_CHKSUM)}, {VIRTCHNL_PROTO_HDR_SCTP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_SRC_PORT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_DST_PORT) | FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_CHKSUM), ICE_FLOW_HASH_SCTP_PORT | BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_CHKSUM)}, {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)}, {VIRTCHNL_PROTO_HDR_GTPC, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_GTPC_TEID), BIT_ULL(ICE_FLOW_FIELD_IDX_GTPC_TEID)}, {VIRTCHNL_PROTO_HDR_L2TPV2, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_L2TPV2_SESS_ID), BIT_ULL(ICE_FLOW_FIELD_IDX_L2TPV2_SESS_ID)}, {VIRTCHNL_PROTO_HDR_L2TPV2, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_L2TPV2_LEN_SESS_ID), BIT_ULL(ICE_FLOW_FIELD_IDX_L2TPV2_LEN_SESS_ID)}, }; static int ice_vc_rss_hash_update(struct ice_hw *hw, struct ice_vsi *vsi, u8 hash_type) { struct ice_vsi_ctx *ctx; int ret; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; /* clear previous hash_type */ ctx->info.q_opt_rss = vsi->info.q_opt_rss & ~ICE_AQ_VSI_Q_OPT_RSS_HASH_M; /* hash_type is passed in as ICE_AQ_VSI_Q_OPT_RSS_info.q_opt_rss |= FIELD_PREP(ICE_AQ_VSI_Q_OPT_RSS_HASH_M, hash_type); /* Preserve existing queueing option setting */ ctx->info.q_opt_tc = vsi->info.q_opt_tc; ctx->info.q_opt_flags = vsi->info.q_opt_flags; ctx->info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_Q_OPT_VALID); ret = ice_update_vsi(hw, vsi->idx, ctx, NULL); if (ret) { dev_err(ice_hw_to_dev(hw), "update VSI for RSS failed, err %d aq_err %s\n", ret, libie_aq_str(hw->adminq.sq_last_status)); } else { vsi->info.q_opt_rss = ctx->info.q_opt_rss; } kfree(ctx); return ret; } /** * 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; bool outer_ipv4 = false; bool outer_ipv6 = false; bool inner_hdr = false; bool has_gre = false; 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]; u32 hdr_found = 0; int j; /* Find matched ice headers according to virtchnl headers. * Also figure out the outer type of GTPU 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) hdr_found = hdr_map.ice_hdr; } 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; } } if (proto_hdr->type == VIRTCHNL_PROTO_HDR_IPV4 && !inner_hdr) outer_ipv4 = true; else if (proto_hdr->type == VIRTCHNL_PROTO_HDR_IPV6 && !inner_hdr) outer_ipv6 = true; /* for GRE and L2TPv2, take inner header as input set if no * any field is selected from outer headers. * for GTPU, take inner header and GTPU teid as input set. */ else if ((proto_hdr->type == VIRTCHNL_PROTO_HDR_GTPU_IP || proto_hdr->type == VIRTCHNL_PROTO_HDR_GTPU_EH || proto_hdr->type == VIRTCHNL_PROTO_HDR_GTPU_EH_PDU_DWN || proto_hdr->type == VIRTCHNL_PROTO_HDR_GTPU_EH_PDU_UP) || ((proto_hdr->type == VIRTCHNL_PROTO_HDR_L2TPV2 || proto_hdr->type == VIRTCHNL_PROTO_HDR_GRE) && *hash_flds == 0)) { /* set inner_hdr flag, and clean up outer header */ inner_hdr = true; /* clear outer headers */ *addl_hdrs = 0; if (outer_ipv4 && outer_ipv6) return false; if (outer_ipv4) hash_cfg->hdr_type = ICE_RSS_INNER_HEADERS_W_OUTER_IPV4; else if (outer_ipv6) hash_cfg->hdr_type = ICE_RSS_INNER_HEADERS_W_OUTER_IPV6; else hash_cfg->hdr_type = ICE_RSS_INNER_HEADERS; if (has_gre && outer_ipv4) hash_cfg->hdr_type = ICE_RSS_INNER_HEADERS_W_OUTER_IPV4_GRE; if (has_gre && outer_ipv6) hash_cfg->hdr_type = ICE_RSS_INNER_HEADERS_W_OUTER_IPV6_GRE; if (proto_hdr->type == VIRTCHNL_PROTO_HDR_GRE) has_gre = true; } *addl_hdrs |= hdr_found; /* refine hash hdrs and fields for IP fragment */ if (VIRTCHNL_TEST_PROTO_HDR_FIELD(proto_hdr, VIRTCHNL_PROTO_HDR_IPV4_FRAG_PKID) && proto_hdr->type == VIRTCHNL_PROTO_HDR_IPV4_FRAG) { *addl_hdrs |= ICE_FLOW_SEG_HDR_IPV_FRAG; *addl_hdrs &= ~(ICE_FLOW_SEG_HDR_IPV_OTHER); *hash_flds |= BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_ID); VIRTCHNL_DEL_PROTO_HDR_FIELD(proto_hdr, VIRTCHNL_PROTO_HDR_IPV4_FRAG_PKID); } if (VIRTCHNL_TEST_PROTO_HDR_FIELD(proto_hdr, VIRTCHNL_PROTO_HDR_IPV6_EH_FRAG_PKID) && proto_hdr->type == VIRTCHNL_PROTO_HDR_IPV6_EH_FRAG) { *addl_hdrs |= ICE_FLOW_SEG_HDR_IPV_FRAG; *addl_hdrs &= ~(ICE_FLOW_SEG_HDR_IPV_OTHER); *hash_flds |= BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_ID); VIRTCHNL_DEL_PROTO_HDR_FIELD(proto_hdr, VIRTCHNL_PROTO_HDR_IPV6_EH_FRAG_PKID); } } /* refine gtpu header if we take outer as input set for a no inner * ip gtpu flow. */ if (hash_cfg->hdr_type == ICE_RSS_OUTER_HEADERS && *addl_hdrs & ICE_FLOW_SEG_HDR_GTPU_IP) { *addl_hdrs &= ~(ICE_FLOW_SEG_HDR_GTPU_IP); *addl_hdrs |= ICE_FLOW_SEG_HDR_GTPU_NON_IP; } /* refine hash field for esp and nat-t-esp. */ if ((*addl_hdrs & ICE_FLOW_SEG_HDR_UDP) && (*addl_hdrs & ICE_FLOW_SEG_HDR_ESP)) { *addl_hdrs &= ~(ICE_FLOW_SEG_HDR_ESP | ICE_FLOW_SEG_HDR_UDP); *addl_hdrs |= ICE_FLOW_SEG_HDR_NAT_T_ESP; *hash_flds &= ~(BIT_ULL(ICE_FLOW_FIELD_IDX_ESP_SPI)); *hash_flds |= BIT_ULL(ICE_FLOW_FIELD_IDX_NAT_T_ESP_SPI); } /* refine hash hdrs for L4 udp/tcp/sctp. */ if (*addl_hdrs & (ICE_FLOW_SEG_HDR_TCP | ICE_FLOW_SEG_HDR_UDP | ICE_FLOW_SEG_HDR_SCTP) && *addl_hdrs & ICE_FLOW_SEG_HDR_IPV_OTHER) *addl_hdrs &= ~ICE_FLOW_SEG_HDR_IPV_OTHER; 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_is_hash_cfg_valid - Check whether an RSS hash context is valid * @cfg: RSS hash configuration to test * * Return: true if both @cfg->hash_flds and @cfg->addl_hdrs are non-zero; false otherwise. */ static bool ice_is_hash_cfg_valid(struct ice_rss_hash_cfg *cfg) { return cfg->hash_flds && cfg->addl_hdrs; } /** * ice_hash_cfg_reset - Reset an RSS hash context * @cfg: RSS hash configuration to reset * * Reset fields of @cfg that store the active rule information. */ static void ice_hash_cfg_reset(struct ice_rss_hash_cfg *cfg) { cfg->hash_flds = 0; cfg->addl_hdrs = 0; cfg->hdr_type = ICE_RSS_OUTER_HEADERS; cfg->symm = 0; } /** * ice_hash_cfg_record - Record an RSS hash context * @ctx: destination (global) RSS hash configuration * @cfg: source RSS hash configuration to record * * Copy the active rule information from @cfg into @ctx. */ static void ice_hash_cfg_record(struct ice_rss_hash_cfg *ctx, struct ice_rss_hash_cfg *cfg) { ctx->hash_flds = cfg->hash_flds; ctx->addl_hdrs = cfg->addl_hdrs; ctx->hdr_type = cfg->hdr_type; ctx->symm = cfg->symm; } /** * ice_hash_moveout - Delete an RSS configuration (keep context) * @vf: VF pointer * @cfg: RSS hash configuration * * Return: 0 on success (including when already absent); -ENOENT if @cfg is * invalid or VSI is missing; -EBUSY on hardware removal failure. */ static int ice_hash_moveout(struct ice_vf *vf, struct ice_rss_hash_cfg *cfg) { struct device *dev = ice_pf_to_dev(vf->pf); struct ice_vsi *vsi = ice_get_vf_vsi(vf); struct ice_hw *hw = &vf->pf->hw; int ret; if (!ice_is_hash_cfg_valid(cfg) || !vsi) return -ENOENT; ret = ice_rem_rss_cfg(hw, vsi->idx, cfg); if (ret && ret != -ENOENT) { dev_err(dev, "ice_rem_rss_cfg failed for VF %d, VSI %d, error:%d\n", vf->vf_id, vf->lan_vsi_idx, ret); return -EBUSY; } return 0; } /** * ice_hash_moveback - Add an RSS hash configuration for a VF * @vf: VF pointer * @cfg: RSS hash configuration to apply * * Add @cfg to @vf if the context is valid and VSI exists; programs HW. * * Return: * * 0 on success * * -ENOENT if @cfg is invalid or VSI is missing * * -EBUSY if hardware programming fails */ static int ice_hash_moveback(struct ice_vf *vf, struct ice_rss_hash_cfg *cfg) { struct device *dev = ice_pf_to_dev(vf->pf); struct ice_vsi *vsi = ice_get_vf_vsi(vf); struct ice_hw *hw = &vf->pf->hw; int ret; if (!ice_is_hash_cfg_valid(cfg) || !vsi) return -ENOENT; ret = ice_add_rss_cfg(hw, vsi, cfg); if (ret) { dev_err(dev, "ice_add_rss_cfg failed for VF %d, VSI %d, error:%d\n", vf->vf_id, vf->lan_vsi_idx, ret); return -EBUSY; } return 0; } /** * ice_hash_remove - remove a RSS configuration * @vf: pointer to the VF info * @cfg: pointer to the RSS hash configuration * * This function will delete a RSS hash configuration and also delete the * hash context which stores the rule info. * * Return: 0 on success, or a negative error code on failure. */ static int ice_hash_remove(struct ice_vf *vf, struct ice_rss_hash_cfg *cfg) { int ret; ret = ice_hash_moveout(vf, cfg); if (ret && ret != -ENOENT) return ret; ice_hash_cfg_reset(cfg); return 0; } struct ice_gtpu_ctx_action { u32 ctx_idx; const u32 *remove_list; int remove_count; const u32 *moveout_list; int moveout_count; }; /** * ice_add_rss_cfg_pre_gtpu - Pre-process the GTPU RSS configuration * @vf: pointer to the VF info * @ctx: pointer to the context of the GTPU hash * @ctx_idx: index of the hash context * * Pre-processes the GTPU hash configuration before adding a new * hash context. It removes or reorders existing hash configurations that may * conflict with the new one. For example, if a GTPU_UP or GTPU_DWN rule is * configured after a GTPU_EH rule, the GTPU_EH hash will be matched first due * to TCAM write and match order (top-down). In such cases, the GTPU_EH rule * must be moved after the GTPU_UP/DWN rule. Conversely, if a GTPU_EH rule is * configured after a GTPU_UP/DWN rule, the UP/DWN rules should be removed to * avoid conflict. * * Return: 0 on success or a negative error code on failure */ static int ice_add_rss_cfg_pre_gtpu(struct ice_vf *vf, struct ice_vf_hash_gtpu_ctx *ctx, u32 ctx_idx) { int ret, i; static const u32 remove_eh_ip[] = { ICE_HASH_GTPU_CTX_EH_IP_UDP, ICE_HASH_GTPU_CTX_EH_IP_TCP, ICE_HASH_GTPU_CTX_UP_IP, ICE_HASH_GTPU_CTX_UP_IP_UDP, ICE_HASH_GTPU_CTX_UP_IP_TCP, ICE_HASH_GTPU_CTX_DW_IP, ICE_HASH_GTPU_CTX_DW_IP_UDP, ICE_HASH_GTPU_CTX_DW_IP_TCP, }; static const u32 remove_eh_ip_udp[] = { ICE_HASH_GTPU_CTX_UP_IP_UDP, ICE_HASH_GTPU_CTX_DW_IP_UDP, }; static const u32 moveout_eh_ip_udp[] = { ICE_HASH_GTPU_CTX_UP_IP, ICE_HASH_GTPU_CTX_UP_IP_TCP, ICE_HASH_GTPU_CTX_DW_IP, ICE_HASH_GTPU_CTX_DW_IP_TCP, }; static const u32 remove_eh_ip_tcp[] = { ICE_HASH_GTPU_CTX_UP_IP_TCP, ICE_HASH_GTPU_CTX_DW_IP_TCP, }; static const u32 moveout_eh_ip_tcp[] = { ICE_HASH_GTPU_CTX_UP_IP, ICE_HASH_GTPU_CTX_UP_IP_UDP, ICE_HASH_GTPU_CTX_DW_IP, ICE_HASH_GTPU_CTX_DW_IP_UDP, }; static const u32 remove_up_ip[] = { ICE_HASH_GTPU_CTX_UP_IP_UDP, ICE_HASH_GTPU_CTX_UP_IP_TCP, }; static const u32 moveout_up_ip[] = { ICE_HASH_GTPU_CTX_EH_IP, ICE_HASH_GTPU_CTX_EH_IP_UDP, ICE_HASH_GTPU_CTX_EH_IP_TCP, }; static const u32 moveout_up_ip_udp_tcp[] = { ICE_HASH_GTPU_CTX_EH_IP, ICE_HASH_GTPU_CTX_EH_IP_UDP, ICE_HASH_GTPU_CTX_EH_IP_TCP, }; static const u32 remove_dw_ip[] = { ICE_HASH_GTPU_CTX_DW_IP_UDP, ICE_HASH_GTPU_CTX_DW_IP_TCP, }; static const u32 moveout_dw_ip[] = { ICE_HASH_GTPU_CTX_EH_IP, ICE_HASH_GTPU_CTX_EH_IP_UDP, ICE_HASH_GTPU_CTX_EH_IP_TCP, }; static const struct ice_gtpu_ctx_action actions[] = { { ICE_HASH_GTPU_CTX_EH_IP, remove_eh_ip, ARRAY_SIZE(remove_eh_ip), NULL, 0 }, { ICE_HASH_GTPU_CTX_EH_IP_UDP, remove_eh_ip_udp, ARRAY_SIZE(remove_eh_ip_udp), moveout_eh_ip_udp, ARRAY_SIZE(moveout_eh_ip_udp) }, { ICE_HASH_GTPU_CTX_EH_IP_TCP, remove_eh_ip_tcp, ARRAY_SIZE(remove_eh_ip_tcp), moveout_eh_ip_tcp, ARRAY_SIZE(moveout_eh_ip_tcp) }, { ICE_HASH_GTPU_CTX_UP_IP, remove_up_ip, ARRAY_SIZE(remove_up_ip), moveout_up_ip, ARRAY_SIZE(moveout_up_ip) }, { ICE_HASH_GTPU_CTX_UP_IP_UDP, NULL, 0, moveout_up_ip_udp_tcp, ARRAY_SIZE(moveout_up_ip_udp_tcp) }, { ICE_HASH_GTPU_CTX_UP_IP_TCP, NULL, 0, moveout_up_ip_udp_tcp, ARRAY_SIZE(moveout_up_ip_udp_tcp) }, { ICE_HASH_GTPU_CTX_DW_IP, remove_dw_ip, ARRAY_SIZE(remove_dw_ip), moveout_dw_ip, ARRAY_SIZE(moveout_dw_ip) }, { ICE_HASH_GTPU_CTX_DW_IP_UDP, NULL, 0, moveout_dw_ip, ARRAY_SIZE(moveout_dw_ip) }, { ICE_HASH_GTPU_CTX_DW_IP_TCP, NULL, 0, moveout_dw_ip, ARRAY_SIZE(moveout_dw_ip) }, }; for (i = 0; i < ARRAY_SIZE(actions); i++) { if (actions[i].ctx_idx != ctx_idx) continue; if (actions[i].remove_list) { for (int j = 0; j < actions[i].remove_count; j++) { u16 rm = actions[i].remove_list[j]; ret = ice_hash_remove(vf, &ctx->ctx[rm]); if (ret && ret != -ENOENT) return ret; } } if (actions[i].moveout_list) { for (int j = 0; j < actions[i].moveout_count; j++) { u16 mv = actions[i].moveout_list[j]; ret = ice_hash_moveout(vf, &ctx->ctx[mv]); if (ret && ret != -ENOENT) return ret; } } break; } return 0; } /** * ice_add_rss_cfg_pre_ip - Pre-process IP-layer RSS configuration * @vf: VF pointer * @ctx: IP L4 hash context (ESP/UDP-ESP/AH/PFCP and UDP/TCP/SCTP) * * Remove covered/recorded IP RSS configurations prior to adding a new one. * * Return: 0 on success; negative error code on failure. */ static int ice_add_rss_cfg_pre_ip(struct ice_vf *vf, struct ice_vf_hash_ip_ctx *ctx) { int i, ret; for (i = 1; i < ICE_HASH_IP_CTX_MAX; i++) if (ice_is_hash_cfg_valid(&ctx->ctx[i])) { ret = ice_hash_remove(vf, &ctx->ctx[i]); if (ret) return ret; } return 0; } /** * ice_calc_gtpu_ctx_idx - Calculate GTPU hash context index * @hdrs: Bitmask of protocol headers prefixed with ICE_FLOW_SEG_HDR_* * * Determine the GTPU hash context index based on the combination of * encapsulation headers (GTPU_EH, GTPU_UP, GTPU_DWN) and transport * protocols (UDP, TCP) within IPv4 or IPv6 flows. * * Return: A valid context index (0-8) if the header combination is supported, * or ICE_HASH_GTPU_CTX_MAX if the combination is invalid. */ static enum ice_hash_gtpu_ctx_type ice_calc_gtpu_ctx_idx(u32 hdrs) { u32 eh_idx, ip_idx; if (hdrs & ICE_FLOW_SEG_HDR_GTPU_EH) eh_idx = 0; else if (hdrs & ICE_FLOW_SEG_HDR_GTPU_UP) eh_idx = 1; else if (hdrs & ICE_FLOW_SEG_HDR_GTPU_DWN) eh_idx = 2; else return ICE_HASH_GTPU_CTX_MAX; ip_idx = 0; if (hdrs & ICE_FLOW_SEG_HDR_UDP) ip_idx = 1; else if (hdrs & ICE_FLOW_SEG_HDR_TCP) ip_idx = 2; if (hdrs & (ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV6)) return eh_idx * 3 + ip_idx; else return ICE_HASH_GTPU_CTX_MAX; } /** * ice_map_ip_ctx_idx - map the index of the IP L4 hash context * @hdrs: protocol headers prefix with ICE_FLOW_SEG_HDR_XXX. * * The IP L4 hash context use the index to classify for IPv4/IPv6 with * ESP/UDP_ESP/AH/PFCP and non-tunnel UDP/TCP/SCTP * this function map the index based on the protocol headers. * * Return: The mapped IP context index on success, or ICE_HASH_IP_CTX_MAX * if no matching context is found. */ static u8 ice_map_ip_ctx_idx(u32 hdrs) { u8 i; static struct { u32 hdrs; u8 ctx_idx; } ip_ctx_idx_map[] = { { ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV_OTHER | ICE_FLOW_SEG_HDR_ESP, ICE_HASH_IP_CTX_IP_ESP }, { ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV_OTHER | ICE_FLOW_SEG_HDR_NAT_T_ESP, ICE_HASH_IP_CTX_IP_UDP_ESP }, { ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV_OTHER | ICE_FLOW_SEG_HDR_AH, ICE_HASH_IP_CTX_IP_AH }, { ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV_OTHER | ICE_FLOW_SEG_HDR_PFCP_SESSION, ICE_HASH_IP_CTX_IP_PFCP }, { ICE_FLOW_SEG_HDR_ETH | ICE_FLOW_SEG_HDR_VLAN | ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_UDP, ICE_HASH_IP_CTX_IP_UDP }, { ICE_FLOW_SEG_HDR_ETH | ICE_FLOW_SEG_HDR_VLAN | ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_TCP, ICE_HASH_IP_CTX_IP_TCP }, { ICE_FLOW_SEG_HDR_ETH | ICE_FLOW_SEG_HDR_VLAN | ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_SCTP, ICE_HASH_IP_CTX_IP_SCTP }, { ICE_FLOW_SEG_HDR_ETH | ICE_FLOW_SEG_HDR_VLAN | ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV_OTHER, ICE_HASH_IP_CTX_IP }, { ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_IPV_OTHER | ICE_FLOW_SEG_HDR_ESP, ICE_HASH_IP_CTX_IP_ESP }, { ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_IPV_OTHER | ICE_FLOW_SEG_HDR_NAT_T_ESP, ICE_HASH_IP_CTX_IP_UDP_ESP }, { ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_IPV_OTHER | ICE_FLOW_SEG_HDR_AH, ICE_HASH_IP_CTX_IP_AH }, { ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_IPV_OTHER | ICE_FLOW_SEG_HDR_PFCP_SESSION, ICE_HASH_IP_CTX_IP_PFCP }, { ICE_FLOW_SEG_HDR_ETH | ICE_FLOW_SEG_HDR_VLAN | ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_UDP, ICE_HASH_IP_CTX_IP_UDP }, { ICE_FLOW_SEG_HDR_ETH | ICE_FLOW_SEG_HDR_VLAN | ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_TCP, ICE_HASH_IP_CTX_IP_TCP }, { ICE_FLOW_SEG_HDR_ETH | ICE_FLOW_SEG_HDR_VLAN | ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_SCTP, ICE_HASH_IP_CTX_IP_SCTP }, { ICE_FLOW_SEG_HDR_ETH | ICE_FLOW_SEG_HDR_VLAN | ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_IPV_OTHER, ICE_HASH_IP_CTX_IP }, /* the remaining mappings are used for default RSS */ { ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_UDP, ICE_HASH_IP_CTX_IP_UDP }, { ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_TCP, ICE_HASH_IP_CTX_IP_TCP }, { ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_SCTP, ICE_HASH_IP_CTX_IP_SCTP }, { ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV_OTHER, ICE_HASH_IP_CTX_IP }, { ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_UDP, ICE_HASH_IP_CTX_IP_UDP }, { ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_TCP, ICE_HASH_IP_CTX_IP_TCP }, { ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_SCTP, ICE_HASH_IP_CTX_IP_SCTP }, { ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_IPV_OTHER, ICE_HASH_IP_CTX_IP }, }; for (i = 0; i < ARRAY_SIZE(ip_ctx_idx_map); i++) { if (hdrs == ip_ctx_idx_map[i].hdrs) return ip_ctx_idx_map[i].ctx_idx; } return ICE_HASH_IP_CTX_MAX; } /** * ice_add_rss_cfg_pre - Prepare RSS configuration context for a VF * @vf: pointer to the VF structure * @cfg: pointer to the RSS hash configuration * * Prepare the RSS hash context for a given VF based on the additional * protocol headers specified in @cfg. This includes pre-configuration * for IP and GTPU-based flows. * * If the configuration matches a known IP context, the function sets up * the appropriate IP hash context. If the configuration includes GTPU * headers, it prepares the GTPU-specific context accordingly. * * Return: 0 on success, or a negative error code on failure. */ static int ice_add_rss_cfg_pre(struct ice_vf *vf, struct ice_rss_hash_cfg *cfg) { u32 ice_gtpu_ctx_idx = ice_calc_gtpu_ctx_idx(cfg->addl_hdrs); u8 ip_ctx_idx = ice_map_ip_ctx_idx(cfg->addl_hdrs); if (ip_ctx_idx == ICE_HASH_IP_CTX_IP) { int ret = 0; if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV4) ret = ice_add_rss_cfg_pre_ip(vf, &vf->hash_ctx.v4); else if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV6) ret = ice_add_rss_cfg_pre_ip(vf, &vf->hash_ctx.v6); if (ret) return ret; } if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV4) { return ice_add_rss_cfg_pre_gtpu(vf, &vf->hash_ctx.ipv4, ice_gtpu_ctx_idx); } else if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV6) { return ice_add_rss_cfg_pre_gtpu(vf, &vf->hash_ctx.ipv6, ice_gtpu_ctx_idx); } return 0; } /** * ice_add_rss_cfg_post_gtpu - Post-process GTPU RSS configuration * @vf: pointer to the VF info * @ctx: pointer to the context of the GTPU hash * @cfg: pointer to the RSS hash configuration * @ctx_idx: index of the hash context * * Post-processes the GTPU hash configuration after a new hash * context has been successfully added. It updates the context with the new * configuration and restores any previously removed hash contexts that need * to be re-applied. This ensures proper TCAM rule ordering and avoids * conflicts between overlapping GTPU rules. * * Return: 0 on success or a negative error code on failure */ static int ice_add_rss_cfg_post_gtpu(struct ice_vf *vf, struct ice_vf_hash_gtpu_ctx *ctx, struct ice_rss_hash_cfg *cfg, u32 ctx_idx) { /* GTPU hash moveback lookup table indexed by context ID. * Each entry is a bitmap indicating which contexts need moveback * operations when the corresponding context index is processed. */ static const unsigned long ice_gtpu_moveback_tbl[ICE_HASH_GTPU_CTX_MAX] = { [ICE_HASH_GTPU_CTX_EH_IP] = 0, [ICE_HASH_GTPU_CTX_EH_IP_UDP] = BIT(ICE_HASH_GTPU_CTX_UP_IP) | BIT(ICE_HASH_GTPU_CTX_UP_IP_TCP) | BIT(ICE_HASH_GTPU_CTX_DW_IP) | BIT(ICE_HASH_GTPU_CTX_DW_IP_TCP), [ICE_HASH_GTPU_CTX_EH_IP_TCP] = BIT(ICE_HASH_GTPU_CTX_UP_IP) | BIT(ICE_HASH_GTPU_CTX_UP_IP_UDP) | BIT(ICE_HASH_GTPU_CTX_DW_IP) | BIT(ICE_HASH_GTPU_CTX_DW_IP_UDP), [ICE_HASH_GTPU_CTX_UP_IP] = BIT(ICE_HASH_GTPU_CTX_EH_IP) | BIT(ICE_HASH_GTPU_CTX_EH_IP_UDP) | BIT(ICE_HASH_GTPU_CTX_EH_IP_TCP), [ICE_HASH_GTPU_CTX_UP_IP_UDP] = BIT(ICE_HASH_GTPU_CTX_EH_IP) | BIT(ICE_HASH_GTPU_CTX_EH_IP_UDP) | BIT(ICE_HASH_GTPU_CTX_EH_IP_TCP), [ICE_HASH_GTPU_CTX_UP_IP_TCP] = BIT(ICE_HASH_GTPU_CTX_EH_IP) | BIT(ICE_HASH_GTPU_CTX_EH_IP_UDP) | BIT(ICE_HASH_GTPU_CTX_EH_IP_TCP), [ICE_HASH_GTPU_CTX_DW_IP] = BIT(ICE_HASH_GTPU_CTX_EH_IP) | BIT(ICE_HASH_GTPU_CTX_EH_IP_UDP) | BIT(ICE_HASH_GTPU_CTX_EH_IP_TCP), [ICE_HASH_GTPU_CTX_DW_IP_UDP] = BIT(ICE_HASH_GTPU_CTX_EH_IP) | BIT(ICE_HASH_GTPU_CTX_EH_IP_UDP) | BIT(ICE_HASH_GTPU_CTX_EH_IP_TCP), [ICE_HASH_GTPU_CTX_DW_IP_TCP] = BIT(ICE_HASH_GTPU_CTX_EH_IP) | BIT(ICE_HASH_GTPU_CTX_EH_IP_UDP) | BIT(ICE_HASH_GTPU_CTX_EH_IP_TCP), }; unsigned long moveback_mask; int ret; int i; if (unlikely(ctx_idx >= ICE_HASH_GTPU_CTX_MAX)) return 0; ctx->ctx[ctx_idx].addl_hdrs = cfg->addl_hdrs; ctx->ctx[ctx_idx].hash_flds = cfg->hash_flds; ctx->ctx[ctx_idx].hdr_type = cfg->hdr_type; ctx->ctx[ctx_idx].symm = cfg->symm; moveback_mask = ice_gtpu_moveback_tbl[ctx_idx]; for_each_set_bit(i, &moveback_mask, ICE_HASH_GTPU_CTX_MAX) { ret = ice_hash_moveback(vf, &ctx->ctx[i]); if (ret && ret != -ENOENT) return ret; } return 0; } static int ice_add_rss_cfg_post(struct ice_vf *vf, struct ice_rss_hash_cfg *cfg) { u32 ice_gtpu_ctx_idx = ice_calc_gtpu_ctx_idx(cfg->addl_hdrs); u8 ip_ctx_idx = ice_map_ip_ctx_idx(cfg->addl_hdrs); if (ip_ctx_idx && ip_ctx_idx < ICE_HASH_IP_CTX_MAX) { if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV4) ice_hash_cfg_record(&vf->hash_ctx.v4.ctx[ip_ctx_idx], cfg); else if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV6) ice_hash_cfg_record(&vf->hash_ctx.v6.ctx[ip_ctx_idx], cfg); } if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV4) { return ice_add_rss_cfg_post_gtpu(vf, &vf->hash_ctx.ipv4, cfg, ice_gtpu_ctx_idx); } else if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV6) { return ice_add_rss_cfg_post_gtpu(vf, &vf->hash_ctx.ipv6, cfg, ice_gtpu_ctx_idx); } return 0; } /** * ice_rem_rss_cfg_post - post-process the RSS configuration * @vf: pointer to the VF info * @cfg: pointer to the RSS hash configuration * * Post process the RSS hash configuration after deleting a hash * config. Such as, it will reset the hash context for the GTPU hash. */ static void ice_rem_rss_cfg_post(struct ice_vf *vf, struct ice_rss_hash_cfg *cfg) { u32 ice_gtpu_ctx_idx = ice_calc_gtpu_ctx_idx(cfg->addl_hdrs); u8 ip_ctx_idx = ice_map_ip_ctx_idx(cfg->addl_hdrs); if (ip_ctx_idx && ip_ctx_idx < ICE_HASH_IP_CTX_MAX) { if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV4) ice_hash_cfg_reset(&vf->hash_ctx.v4.ctx[ip_ctx_idx]); else if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV6) ice_hash_cfg_reset(&vf->hash_ctx.v6.ctx[ip_ctx_idx]); } if (ice_gtpu_ctx_idx >= ICE_HASH_GTPU_CTX_MAX) return; if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV4) ice_hash_cfg_reset(&vf->hash_ctx.ipv4.ctx[ice_gtpu_ctx_idx]); else if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV6) ice_hash_cfg_reset(&vf->hash_ctx.ipv6.ctx[ice_gtpu_ctx_idx]); } /** * ice_rem_rss_cfg_wrap - Wrapper for deleting an RSS configuration * @vf: pointer to the VF info * @cfg: pointer to the RSS hash configuration * * Wrapper function to delete a flow profile base on an RSS configuration, * and also post process the hash context base on the rollback mechanism * which handle some rules conflict by ice_add_rss_cfg_wrap. * * Return: 0 on success; negative error code on failure. */ static int ice_rem_rss_cfg_wrap(struct ice_vf *vf, struct ice_rss_hash_cfg *cfg) { struct device *dev = ice_pf_to_dev(vf->pf); struct ice_vsi *vsi = ice_get_vf_vsi(vf); struct ice_hw *hw = &vf->pf->hw; int ret; ret = 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 (ret && ret != -ENOENT) { dev_err(dev, "ice_rem_rss_cfg failed for VF %d, VSI %d, error:%d\n", vf->vf_id, vf->lan_vsi_idx, ret); return ret; } ice_rem_rss_cfg_post(vf, cfg); return 0; } /** * ice_add_rss_cfg_wrap - Wrapper for adding an RSS configuration * @vf: pointer to the VF info * @cfg: pointer to the RSS hash configuration * * Add a flow profile based on an RSS configuration. Use a rollback * mechanism to handle rule conflicts due to TCAM * write sequence from top to down. * * Return: 0 on success; negative error code on failure. */ static int ice_add_rss_cfg_wrap(struct ice_vf *vf, struct ice_rss_hash_cfg *cfg) { struct device *dev = ice_pf_to_dev(vf->pf); struct ice_vsi *vsi = ice_get_vf_vsi(vf); struct ice_hw *hw = &vf->pf->hw; int ret; if (ice_add_rss_cfg_pre(vf, cfg)) return -EINVAL; ret = ice_add_rss_cfg(hw, vsi, cfg); if (ret) { dev_err(dev, "ice_add_rss_cfg failed for VF %d, VSI %d, error:%d\n", vf->vf_id, vf->lan_vsi_idx, ret); return ret; } if (ice_add_rss_cfg_post(vf, cfg)) ret = -EINVAL; return ret; } /** * ice_parse_raw_rss_pattern - Parse raw pattern spec and mask for RSS * @vf: pointer to the VF info * @proto: pointer to the virtchnl protocol header * @raw_cfg: pointer to the RSS raw pattern configuration * * Parser function to get spec and mask from virtchnl message, and parse * them to get the corresponding profile and offset. The profile is used * to add RSS configuration. * * Return: 0 on success; negative error code on failure. */ static int ice_parse_raw_rss_pattern(struct ice_vf *vf, struct virtchnl_proto_hdrs *proto, struct ice_rss_raw_cfg *raw_cfg) { struct ice_parser_result pkt_parsed; struct ice_hw *hw = &vf->pf->hw; struct ice_parser_profile prof; struct ice_parser *psr; u8 *pkt_buf, *msk_buf; u16 pkt_len; int ret = 0; pkt_len = proto->raw.pkt_len; if (!pkt_len) return -EINVAL; if (pkt_len > VIRTCHNL_MAX_SIZE_RAW_PACKET) pkt_len = VIRTCHNL_MAX_SIZE_RAW_PACKET; pkt_buf = kzalloc(pkt_len, GFP_KERNEL); msk_buf = kzalloc(pkt_len, GFP_KERNEL); if (!pkt_buf || !msk_buf) { ret = -ENOMEM; goto free_alloc; } memcpy(pkt_buf, proto->raw.spec, pkt_len); memcpy(msk_buf, proto->raw.mask, pkt_len); psr = ice_parser_create(hw); if (IS_ERR(psr)) { ret = PTR_ERR(psr); goto free_alloc; } ret = ice_parser_run(psr, pkt_buf, pkt_len, &pkt_parsed); if (ret) goto parser_destroy; ret = ice_parser_profile_init(&pkt_parsed, pkt_buf, msk_buf, pkt_len, ICE_BLK_RSS, &prof); if (ret) goto parser_destroy; memcpy(&raw_cfg->prof, &prof, sizeof(prof)); parser_destroy: ice_parser_destroy(psr); free_alloc: kfree(pkt_buf); kfree(msk_buf); return ret; } /** * ice_add_raw_rss_cfg - add RSS configuration for raw pattern * @vf: pointer to the VF info * @cfg: pointer to the RSS raw pattern configuration * * This function adds the RSS configuration for raw pattern. * Check if current profile is matched. If not, remove the old * one and add the new profile to HW directly. Update the symmetric * hash configuration as well. * * Return: 0 on success; negative error code on failure. */ static int ice_add_raw_rss_cfg(struct ice_vf *vf, struct ice_rss_raw_cfg *cfg) { struct ice_parser_profile *prof = &cfg->prof; struct device *dev = ice_pf_to_dev(vf->pf); struct ice_rss_prof_info *rss_prof; struct ice_hw *hw = &vf->pf->hw; int i, ptg, ret = 0; u16 vsi_handle; u64 id; vsi_handle = vf->lan_vsi_idx; id = find_first_bit(prof->ptypes, ICE_FLOW_PTYPE_MAX); ptg = hw->blk[ICE_BLK_RSS].xlt1.t[id]; rss_prof = &vf->rss_prof_info[ptg]; /* check if ptg already has a profile */ if (rss_prof->prof.fv_num) { for (i = 0; i < ICE_MAX_FV_WORDS; i++) { if (rss_prof->prof.fv[i].proto_id != prof->fv[i].proto_id || rss_prof->prof.fv[i].offset != prof->fv[i].offset) break; } /* current profile is matched, check symmetric hash */ if (i == ICE_MAX_FV_WORDS) { if (rss_prof->symm != cfg->symm) goto update_symm; return ret; } /* current profile is not matched, remove it */ ret = ice_rem_prof_id_flow(hw, ICE_BLK_RSS, ice_get_hw_vsi_num(hw, vsi_handle), id); if (ret) { dev_err(dev, "remove RSS flow failed\n"); return ret; } ret = ice_rem_prof(hw, ICE_BLK_RSS, id); if (ret) { dev_err(dev, "remove RSS profile failed\n"); return ret; } } /* add new profile */ ret = ice_flow_set_parser_prof(hw, vsi_handle, 0, prof, ICE_BLK_RSS); if (ret) { dev_err(dev, "HW profile add failed\n"); return ret; } memcpy(&rss_prof->prof, prof, sizeof(struct ice_parser_profile)); update_symm: rss_prof->symm = cfg->symm; ice_rss_update_raw_symm(hw, cfg, id); return ret; } /** * ice_rem_raw_rss_cfg - remove RSS configuration for raw pattern * @vf: pointer to the VF info * @cfg: pointer to the RSS raw pattern configuration * * This function removes the RSS configuration for raw pattern. * Check if vsi group is already removed first. If not, remove the * profile. * * Return: 0 on success; negative error code on failure. */ static int ice_rem_raw_rss_cfg(struct ice_vf *vf, struct ice_rss_raw_cfg *cfg) { struct ice_parser_profile *prof = &cfg->prof; struct device *dev = ice_pf_to_dev(vf->pf); struct ice_hw *hw = &vf->pf->hw; int ptg, ret = 0; u16 vsig, vsi; u64 id; id = find_first_bit(prof->ptypes, ICE_FLOW_PTYPE_MAX); ptg = hw->blk[ICE_BLK_RSS].xlt1.t[id]; memset(&vf->rss_prof_info[ptg], 0, sizeof(struct ice_rss_prof_info)); /* check if vsig is already removed */ vsi = ice_get_hw_vsi_num(hw, vf->lan_vsi_idx); if (vsi >= ICE_MAX_VSI) { ret = -EINVAL; goto err; } vsig = hw->blk[ICE_BLK_RSS].xlt2.vsis[vsi].vsig; if (vsig) { ret = ice_rem_prof_id_flow(hw, ICE_BLK_RSS, vsi, id); if (ret) goto err; ret = ice_rem_prof(hw, ICE_BLK_RSS, id); if (ret) goto err; } return ret; err: dev_err(dev, "HW profile remove failed\n"); return ret; } /** * 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; u8 hash_type; bool symm; int ret; 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 (rss_cfg->rss_algorithm == VIRTCHNL_RSS_ALG_R_ASYMMETRIC) { hash_type = add ? ICE_AQ_VSI_Q_OPT_RSS_HASH_XOR : ICE_AQ_VSI_Q_OPT_RSS_HASH_TPLZ; ret = ice_vc_rss_hash_update(hw, vsi, hash_type); if (ret) v_ret = ice_err_to_virt_err(ret); goto error_param; } hash_type = add ? ICE_AQ_VSI_Q_OPT_RSS_HASH_SYM_TPLZ : ICE_AQ_VSI_Q_OPT_RSS_HASH_TPLZ; ret = ice_vc_rss_hash_update(hw, vsi, hash_type); if (ret) { v_ret = ice_err_to_virt_err(ret); goto error_param; } symm = rss_cfg->rss_algorithm == VIRTCHNL_RSS_ALG_TOEPLITZ_SYMMETRIC; /* Configure RSS hash for raw pattern */ if (rss_cfg->proto_hdrs.tunnel_level == 0 && rss_cfg->proto_hdrs.count == 0) { struct ice_rss_raw_cfg raw_cfg; if (ice_parse_raw_rss_pattern(vf, &rss_cfg->proto_hdrs, &raw_cfg)) { v_ret = VIRTCHNL_STATUS_ERR_PARAM; goto error_param; } if (add) { raw_cfg.symm = symm; if (ice_add_raw_rss_cfg(vf, &raw_cfg)) v_ret = VIRTCHNL_STATUS_ERR_PARAM; } else { if (ice_rem_raw_rss_cfg(vf, &raw_cfg)) v_ret = VIRTCHNL_STATUS_ERR_PARAM; } } 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) { cfg.symm = symm; if (ice_add_rss_cfg_wrap(vf, &cfg)) v_ret = VIRTCHNL_STATUS_ERR_PARAM; } else { if (ice_rem_rss_cfg_wrap(vf, &cfg)) v_ret = VIRTCHNL_STATUS_ERR_PARAM; } } 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); }