forked from mirrors/linux
		
	 99f419df8a
			
		
	
	
		99f419df8a
		
	
	
	
	
		
			
			Enable VFs to create FDIR filters from raw binary patterns. The corresponding processes for raw flow are added in the Parse / Create / Destroy stages. Reviewed-by: Marcin Szycik <marcin.szycik@linux.intel.com> Signed-off-by: Junfeng Guo <junfeng.guo@intel.com> Co-developed-by: Ahmed Zaki <ahmed.zaki@intel.com> Signed-off-by: Ahmed Zaki <ahmed.zaki@intel.com> Tested-by: Rafal Romanowski <rafal.romanowski@intel.com> Reviewed-by: Simon Horman <horms@kernel.org> Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
		
			
				
	
	
		
			2809 lines
		
	
	
	
		
			90 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2809 lines
		
	
	
	
		
			90 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /* Copyright (c) 2019, Intel Corporation. */
 | |
| 
 | |
| #include "ice_common.h"
 | |
| #include "ice_flow.h"
 | |
| #include <net/gre.h>
 | |
| 
 | |
| /* Describe properties of a protocol header field */
 | |
| struct ice_flow_field_info {
 | |
| 	enum ice_flow_seg_hdr hdr;
 | |
| 	s16 off;	/* Offset from start of a protocol header, in bits */
 | |
| 	u16 size;	/* Size of fields in bits */
 | |
| 	u16 mask;	/* 16-bit mask for field */
 | |
| };
 | |
| 
 | |
| #define ICE_FLOW_FLD_INFO(_hdr, _offset_bytes, _size_bytes) { \
 | |
| 	.hdr = _hdr, \
 | |
| 	.off = (_offset_bytes) * BITS_PER_BYTE, \
 | |
| 	.size = (_size_bytes) * BITS_PER_BYTE, \
 | |
| 	.mask = 0, \
 | |
| }
 | |
| 
 | |
| #define ICE_FLOW_FLD_INFO_MSK(_hdr, _offset_bytes, _size_bytes, _mask) { \
 | |
| 	.hdr = _hdr, \
 | |
| 	.off = (_offset_bytes) * BITS_PER_BYTE, \
 | |
| 	.size = (_size_bytes) * BITS_PER_BYTE, \
 | |
| 	.mask = _mask, \
 | |
| }
 | |
| 
 | |
| /* Table containing properties of supported protocol header fields */
 | |
| static const
 | |
| struct ice_flow_field_info ice_flds_info[ICE_FLOW_FIELD_IDX_MAX] = {
 | |
| 	/* Ether */
 | |
| 	/* ICE_FLOW_FIELD_IDX_ETH_DA */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_ETH, 0, ETH_ALEN),
 | |
| 	/* ICE_FLOW_FIELD_IDX_ETH_SA */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_ETH, ETH_ALEN, ETH_ALEN),
 | |
| 	/* ICE_FLOW_FIELD_IDX_S_VLAN */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_VLAN, 12, sizeof(__be16)),
 | |
| 	/* ICE_FLOW_FIELD_IDX_C_VLAN */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_VLAN, 14, sizeof(__be16)),
 | |
| 	/* ICE_FLOW_FIELD_IDX_ETH_TYPE */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_ETH, 0, sizeof(__be16)),
 | |
| 	/* IPv4 / IPv6 */
 | |
| 	/* ICE_FLOW_FIELD_IDX_IPV4_DSCP */
 | |
| 	ICE_FLOW_FLD_INFO_MSK(ICE_FLOW_SEG_HDR_IPV4, 0, 1, 0x00fc),
 | |
| 	/* ICE_FLOW_FIELD_IDX_IPV6_DSCP */
 | |
| 	ICE_FLOW_FLD_INFO_MSK(ICE_FLOW_SEG_HDR_IPV6, 0, 1, 0x0ff0),
 | |
| 	/* ICE_FLOW_FIELD_IDX_IPV4_TTL */
 | |
| 	ICE_FLOW_FLD_INFO_MSK(ICE_FLOW_SEG_HDR_NONE, 8, 1, 0xff00),
 | |
| 	/* ICE_FLOW_FIELD_IDX_IPV4_PROT */
 | |
| 	ICE_FLOW_FLD_INFO_MSK(ICE_FLOW_SEG_HDR_NONE, 8, 1, 0x00ff),
 | |
| 	/* ICE_FLOW_FIELD_IDX_IPV6_TTL */
 | |
| 	ICE_FLOW_FLD_INFO_MSK(ICE_FLOW_SEG_HDR_NONE, 6, 1, 0x00ff),
 | |
| 	/* ICE_FLOW_FIELD_IDX_IPV6_PROT */
 | |
| 	ICE_FLOW_FLD_INFO_MSK(ICE_FLOW_SEG_HDR_NONE, 6, 1, 0xff00),
 | |
| 	/* ICE_FLOW_FIELD_IDX_IPV4_SA */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV4, 12, sizeof(struct in_addr)),
 | |
| 	/* ICE_FLOW_FIELD_IDX_IPV4_DA */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV4, 16, sizeof(struct in_addr)),
 | |
| 	/* ICE_FLOW_FIELD_IDX_IPV6_SA */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV6, 8, sizeof(struct in6_addr)),
 | |
| 	/* ICE_FLOW_FIELD_IDX_IPV6_DA */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV6, 24, sizeof(struct in6_addr)),
 | |
| 	/* Transport */
 | |
| 	/* ICE_FLOW_FIELD_IDX_TCP_SRC_PORT */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_TCP, 0, sizeof(__be16)),
 | |
| 	/* ICE_FLOW_FIELD_IDX_TCP_DST_PORT */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_TCP, 2, sizeof(__be16)),
 | |
| 	/* ICE_FLOW_FIELD_IDX_UDP_SRC_PORT */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_UDP, 0, sizeof(__be16)),
 | |
| 	/* ICE_FLOW_FIELD_IDX_UDP_DST_PORT */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_UDP, 2, sizeof(__be16)),
 | |
| 	/* ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_SCTP, 0, sizeof(__be16)),
 | |
| 	/* ICE_FLOW_FIELD_IDX_SCTP_DST_PORT */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_SCTP, 2, sizeof(__be16)),
 | |
| 	/* ICE_FLOW_FIELD_IDX_TCP_FLAGS */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_TCP, 13, 1),
 | |
| 	/* ARP */
 | |
| 	/* ICE_FLOW_FIELD_IDX_ARP_SIP */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_ARP, 14, sizeof(struct in_addr)),
 | |
| 	/* ICE_FLOW_FIELD_IDX_ARP_DIP */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_ARP, 24, sizeof(struct in_addr)),
 | |
| 	/* ICE_FLOW_FIELD_IDX_ARP_SHA */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_ARP, 8, ETH_ALEN),
 | |
| 	/* ICE_FLOW_FIELD_IDX_ARP_DHA */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_ARP, 18, ETH_ALEN),
 | |
| 	/* ICE_FLOW_FIELD_IDX_ARP_OP */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_ARP, 6, sizeof(__be16)),
 | |
| 	/* ICMP */
 | |
| 	/* ICE_FLOW_FIELD_IDX_ICMP_TYPE */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_ICMP, 0, 1),
 | |
| 	/* ICE_FLOW_FIELD_IDX_ICMP_CODE */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_ICMP, 1, 1),
 | |
| 	/* GRE */
 | |
| 	/* ICE_FLOW_FIELD_IDX_GRE_KEYID */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_GRE, 12,
 | |
| 			  sizeof_field(struct gre_full_hdr, key)),
 | |
| 	/* GTP */
 | |
| 	/* ICE_FLOW_FIELD_IDX_GTPC_TEID */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_GTPC_TEID, 12, sizeof(__be32)),
 | |
| 	/* ICE_FLOW_FIELD_IDX_GTPU_IP_TEID */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_GTPU_IP, 12, sizeof(__be32)),
 | |
| 	/* ICE_FLOW_FIELD_IDX_GTPU_EH_TEID */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_GTPU_EH, 12, sizeof(__be32)),
 | |
| 	/* ICE_FLOW_FIELD_IDX_GTPU_EH_QFI */
 | |
| 	ICE_FLOW_FLD_INFO_MSK(ICE_FLOW_SEG_HDR_GTPU_EH, 22, sizeof(__be16),
 | |
| 			      0x3f00),
 | |
| 	/* ICE_FLOW_FIELD_IDX_GTPU_UP_TEID */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_GTPU_UP, 12, sizeof(__be32)),
 | |
| 	/* ICE_FLOW_FIELD_IDX_GTPU_DWN_TEID */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_GTPU_DWN, 12, sizeof(__be32)),
 | |
| 	/* PPPoE */
 | |
| 	/* ICE_FLOW_FIELD_IDX_PPPOE_SESS_ID */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_PPPOE, 2, sizeof(__be16)),
 | |
| 	/* PFCP */
 | |
| 	/* ICE_FLOW_FIELD_IDX_PFCP_SEID */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_PFCP_SESSION, 12, sizeof(__be64)),
 | |
| 	/* L2TPv3 */
 | |
| 	/* ICE_FLOW_FIELD_IDX_L2TPV3_SESS_ID */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_L2TPV3, 0, sizeof(__be32)),
 | |
| 	/* ESP */
 | |
| 	/* ICE_FLOW_FIELD_IDX_ESP_SPI */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_ESP, 0, sizeof(__be32)),
 | |
| 	/* AH */
 | |
| 	/* ICE_FLOW_FIELD_IDX_AH_SPI */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_AH, 4, sizeof(__be32)),
 | |
| 	/* NAT_T_ESP */
 | |
| 	/* ICE_FLOW_FIELD_IDX_NAT_T_ESP_SPI */
 | |
| 	ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_NAT_T_ESP, 8, sizeof(__be32)),
 | |
| };
 | |
| 
 | |
| /* Bitmaps indicating relevant packet types for a particular protocol header
 | |
|  *
 | |
|  * Packet types for packets with an Outer/First/Single MAC header
 | |
|  */
 | |
| static const u32 ice_ptypes_mac_ofos[] = {
 | |
| 	0xFDC00846, 0xBFBF7F7E, 0xF70001DF, 0xFEFDFDFB,
 | |
| 	0x0000077E, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00400000, 0x03FFF000, 0x7FFFFFE0, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Innermost/Last MAC VLAN header */
 | |
| static const u32 ice_ptypes_macvlan_il[] = {
 | |
| 	0x00000000, 0xBC000000, 0x000001DF, 0xF0000000,
 | |
| 	0x0000077E, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Outer/First/Single IPv4 header, does NOT
 | |
|  * include IPv4 other PTYPEs
 | |
|  */
 | |
| static const u32 ice_ptypes_ipv4_ofos[] = {
 | |
| 	0x1DC00000, 0x04000800, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000155, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x000FC000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Outer/First/Single IPv4 header, includes
 | |
|  * IPv4 other PTYPEs
 | |
|  */
 | |
| static const u32 ice_ptypes_ipv4_ofos_all[] = {
 | |
| 	0x1DC00000, 0x04000800, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000155, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x000FC000, 0x83E0F800, 0x00000101,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Innermost/Last IPv4 header */
 | |
| static const u32 ice_ptypes_ipv4_il[] = {
 | |
| 	0xE0000000, 0xB807700E, 0x80000003, 0xE01DC03B,
 | |
| 	0x0000000E, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x001FF800, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Outer/First/Single IPv6 header, does NOT
 | |
|  * include IPv6 other PTYPEs
 | |
|  */
 | |
| static const u32 ice_ptypes_ipv6_ofos[] = {
 | |
| 	0x00000000, 0x00000000, 0x77000000, 0x10002000,
 | |
| 	0x00000000, 0x000002AA, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x03F00000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Outer/First/Single IPv6 header, includes
 | |
|  * IPv6 other PTYPEs
 | |
|  */
 | |
| static const u32 ice_ptypes_ipv6_ofos_all[] = {
 | |
| 	0x00000000, 0x00000000, 0x77000000, 0x10002000,
 | |
| 	0x00000000, 0x000002AA, 0x00000000, 0x00000000,
 | |
| 	0x00080F00, 0x03F00000, 0x7C1F0000, 0x00000206,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Innermost/Last IPv6 header */
 | |
| static const u32 ice_ptypes_ipv6_il[] = {
 | |
| 	0x00000000, 0x03B80770, 0x000001DC, 0x0EE00000,
 | |
| 	0x00000770, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x7FE00000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Outer/First/Single IPv4 header - no L4 */
 | |
| static const u32 ice_ptypes_ipv4_ofos_no_l4[] = {
 | |
| 	0x10C00000, 0x04000800, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Outermost/First ARP header */
 | |
| static const u32 ice_ptypes_arp_of[] = {
 | |
| 	0x00000800, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Innermost/Last IPv4 header - no L4 */
 | |
| static const u32 ice_ptypes_ipv4_il_no_l4[] = {
 | |
| 	0x60000000, 0x18043008, 0x80000002, 0x6010c021,
 | |
| 	0x00000008, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Outer/First/Single IPv6 header - no L4 */
 | |
| static const u32 ice_ptypes_ipv6_ofos_no_l4[] = {
 | |
| 	0x00000000, 0x00000000, 0x43000000, 0x10002000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Innermost/Last IPv6 header - no L4 */
 | |
| static const u32 ice_ptypes_ipv6_il_no_l4[] = {
 | |
| 	0x00000000, 0x02180430, 0x0000010c, 0x086010c0,
 | |
| 	0x00000430, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* UDP Packet types for non-tunneled packets or tunneled
 | |
|  * packets with inner UDP.
 | |
|  */
 | |
| static const u32 ice_ptypes_udp_il[] = {
 | |
| 	0x81000000, 0x20204040, 0x04000010, 0x80810102,
 | |
| 	0x00000040, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00410000, 0x90842000, 0x00000007,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Innermost/Last TCP header */
 | |
| static const u32 ice_ptypes_tcp_il[] = {
 | |
| 	0x04000000, 0x80810102, 0x10000040, 0x02040408,
 | |
| 	0x00000102, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00820000, 0x21084000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Innermost/Last SCTP header */
 | |
| static const u32 ice_ptypes_sctp_il[] = {
 | |
| 	0x08000000, 0x01020204, 0x20000081, 0x04080810,
 | |
| 	0x00000204, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x01040000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Outermost/First ICMP header */
 | |
| static const u32 ice_ptypes_icmp_of[] = {
 | |
| 	0x10000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Innermost/Last ICMP header */
 | |
| static const u32 ice_ptypes_icmp_il[] = {
 | |
| 	0x00000000, 0x02040408, 0x40000102, 0x08101020,
 | |
| 	0x00000408, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x42108000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Outermost/First GRE header */
 | |
| static const u32 ice_ptypes_gre_of[] = {
 | |
| 	0x00000000, 0xBFBF7800, 0x000001DF, 0xFEFDE000,
 | |
| 	0x0000017E, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with an Innermost/Last MAC header */
 | |
| static const u32 ice_ptypes_mac_il[] = {
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for GTPC */
 | |
| static const u32 ice_ptypes_gtpc[] = {
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000180, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for GTPC with TEID */
 | |
| static const u32 ice_ptypes_gtpc_tid[] = {
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000060, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for GTPU */
 | |
| static const struct ice_ptype_attributes ice_attr_gtpu_session[] = {
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_FRAG,	  ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_PAY,	  ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_UDP_PAY, ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_TCP,	  ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_ICMP,	  ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_FRAG,	  ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_PAY,	  ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_UDP_PAY, ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_TCP,	  ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_ICMP,	  ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_FRAG,	  ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_PAY,	  ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_UDP_PAY, ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_TCP,	  ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_ICMPV6,  ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_FRAG,	  ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_PAY,	  ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_UDP_PAY, ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_TCP,	  ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_ICMPV6,  ICE_PTYPE_ATTR_GTP_SESSION },
 | |
| };
 | |
| 
 | |
| static const struct ice_ptype_attributes ice_attr_gtpu_eh[] = {
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_FRAG,	  ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_PAY,	  ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_UDP_PAY, ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_TCP,	  ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_ICMP,	  ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_FRAG,	  ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_PAY,	  ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_UDP_PAY, ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_TCP,	  ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_ICMP,	  ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_FRAG,	  ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_PAY,	  ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_UDP_PAY, ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_TCP,	  ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_ICMPV6,  ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_FRAG,	  ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_PAY,	  ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_UDP_PAY, ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_TCP,	  ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_ICMPV6,  ICE_PTYPE_ATTR_GTP_PDU_EH },
 | |
| };
 | |
| 
 | |
| static const struct ice_ptype_attributes ice_attr_gtpu_down[] = {
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_FRAG,	  ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_PAY,	  ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_UDP_PAY, ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_TCP,	  ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_ICMP,	  ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_FRAG,	  ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_PAY,	  ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_UDP_PAY, ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_TCP,	  ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_ICMP,	  ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_FRAG,	  ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_PAY,	  ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_UDP_PAY, ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_TCP,	  ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_ICMPV6,  ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_FRAG,	  ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_PAY,	  ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_UDP_PAY, ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_TCP,	  ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_ICMPV6,  ICE_PTYPE_ATTR_GTP_DOWNLINK },
 | |
| };
 | |
| 
 | |
| static const struct ice_ptype_attributes ice_attr_gtpu_up[] = {
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_FRAG,	  ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_PAY,	  ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_UDP_PAY, ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_TCP,	  ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV4_ICMP,	  ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_FRAG,	  ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_PAY,	  ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_UDP_PAY, ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_TCP,	  ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV4_ICMP,	  ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_FRAG,	  ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_PAY,	  ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_UDP_PAY, ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_TCP,	  ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV4_GTPU_IPV6_ICMPV6,  ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_FRAG,	  ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_PAY,	  ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_UDP_PAY, ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_TCP,	  ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| 	{ ICE_MAC_IPV6_GTPU_IPV6_ICMPV6,  ICE_PTYPE_ATTR_GTP_UPLINK },
 | |
| };
 | |
| 
 | |
| static const u32 ice_ptypes_gtpu[] = {
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x7FFFFE00, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for PPPoE */
 | |
| static const u32 ice_ptypes_pppoe[] = {
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x03ffe000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with PFCP NODE header */
 | |
| static const u32 ice_ptypes_pfcp_node[] = {
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x80000000, 0x00000002,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with PFCP SESSION header */
 | |
| static const u32 ice_ptypes_pfcp_session[] = {
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000005,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for L2TPv3 */
 | |
| static const u32 ice_ptypes_l2tpv3[] = {
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000300,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for ESP */
 | |
| static const u32 ice_ptypes_esp[] = {
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000003, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for AH */
 | |
| static const u32 ice_ptypes_ah[] = {
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x0000000C, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Packet types for packets with NAT_T ESP header */
 | |
| static const u32 ice_ptypes_nat_t_esp[] = {
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000030, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| static const u32 ice_ptypes_mac_non_ip_ofos[] = {
 | |
| 	0x00000846, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00400000, 0x03FFF000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| 	0x00000000, 0x00000000, 0x00000000, 0x00000000,
 | |
| };
 | |
| 
 | |
| /* Manage parameters and info. used during the creation of a flow profile */
 | |
| struct ice_flow_prof_params {
 | |
| 	enum ice_block blk;
 | |
| 	u16 entry_length; /* # of bytes formatted entry will require */
 | |
| 	u8 es_cnt;
 | |
| 	struct ice_flow_prof *prof;
 | |
| 
 | |
| 	/* For ACL, the es[0] will have the data of ICE_RX_MDID_PKT_FLAGS_15_0
 | |
| 	 * This will give us the direction flags.
 | |
| 	 */
 | |
| 	struct ice_fv_word es[ICE_MAX_FV_WORDS];
 | |
| 	/* attributes can be used to add attributes to a particular PTYPE */
 | |
| 	const struct ice_ptype_attributes *attr;
 | |
| 	u16 attr_cnt;
 | |
| 
 | |
| 	u16 mask[ICE_MAX_FV_WORDS];
 | |
| 	DECLARE_BITMAP(ptypes, ICE_FLOW_PTYPE_MAX);
 | |
| };
 | |
| 
 | |
| #define ICE_FLOW_RSS_HDRS_INNER_MASK \
 | |
| 	(ICE_FLOW_SEG_HDR_PPPOE | ICE_FLOW_SEG_HDR_GTPC | \
 | |
| 	ICE_FLOW_SEG_HDR_GTPC_TEID | ICE_FLOW_SEG_HDR_GTPU | \
 | |
| 	ICE_FLOW_SEG_HDR_PFCP_SESSION | ICE_FLOW_SEG_HDR_L2TPV3 | \
 | |
| 	ICE_FLOW_SEG_HDR_ESP | ICE_FLOW_SEG_HDR_AH | \
 | |
| 	ICE_FLOW_SEG_HDR_NAT_T_ESP)
 | |
| 
 | |
| #define ICE_FLOW_SEG_HDRS_L3_MASK	\
 | |
| 	(ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_ARP)
 | |
| #define ICE_FLOW_SEG_HDRS_L4_MASK	\
 | |
| 	(ICE_FLOW_SEG_HDR_ICMP | ICE_FLOW_SEG_HDR_TCP | ICE_FLOW_SEG_HDR_UDP | \
 | |
| 	 ICE_FLOW_SEG_HDR_SCTP)
 | |
| /* mask for L4 protocols that are NOT part of IPv4/6 OTHER PTYPE groups */
 | |
| #define ICE_FLOW_SEG_HDRS_L4_MASK_NO_OTHER	\
 | |
| 	(ICE_FLOW_SEG_HDR_TCP | ICE_FLOW_SEG_HDR_UDP | ICE_FLOW_SEG_HDR_SCTP)
 | |
| 
 | |
| /**
 | |
|  * ice_flow_val_hdrs - validates packet segments for valid protocol headers
 | |
|  * @segs: array of one or more packet segments that describe the flow
 | |
|  * @segs_cnt: number of packet segments provided
 | |
|  */
 | |
| static int ice_flow_val_hdrs(struct ice_flow_seg_info *segs, u8 segs_cnt)
 | |
| {
 | |
| 	u8 i;
 | |
| 
 | |
| 	for (i = 0; i < segs_cnt; i++) {
 | |
| 		/* Multiple L3 headers */
 | |
| 		if (segs[i].hdrs & ICE_FLOW_SEG_HDRS_L3_MASK &&
 | |
| 		    !is_power_of_2(segs[i].hdrs & ICE_FLOW_SEG_HDRS_L3_MASK))
 | |
| 			return -EINVAL;
 | |
| 
 | |
| 		/* Multiple L4 headers */
 | |
| 		if (segs[i].hdrs & ICE_FLOW_SEG_HDRS_L4_MASK &&
 | |
| 		    !is_power_of_2(segs[i].hdrs & ICE_FLOW_SEG_HDRS_L4_MASK))
 | |
| 			return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* Sizes of fixed known protocol headers without header options */
 | |
| #define ICE_FLOW_PROT_HDR_SZ_MAC	14
 | |
| #define ICE_FLOW_PROT_HDR_SZ_MAC_VLAN	(ICE_FLOW_PROT_HDR_SZ_MAC + 2)
 | |
| #define ICE_FLOW_PROT_HDR_SZ_IPV4	20
 | |
| #define ICE_FLOW_PROT_HDR_SZ_IPV6	40
 | |
| #define ICE_FLOW_PROT_HDR_SZ_ARP	28
 | |
| #define ICE_FLOW_PROT_HDR_SZ_ICMP	8
 | |
| #define ICE_FLOW_PROT_HDR_SZ_TCP	20
 | |
| #define ICE_FLOW_PROT_HDR_SZ_UDP	8
 | |
| #define ICE_FLOW_PROT_HDR_SZ_SCTP	12
 | |
| 
 | |
| /**
 | |
|  * ice_flow_calc_seg_sz - calculates size of a packet segment based on headers
 | |
|  * @params: information about the flow to be processed
 | |
|  * @seg: index of packet segment whose header size is to be determined
 | |
|  */
 | |
| static u16 ice_flow_calc_seg_sz(struct ice_flow_prof_params *params, u8 seg)
 | |
| {
 | |
| 	u16 sz;
 | |
| 
 | |
| 	/* L2 headers */
 | |
| 	sz = (params->prof->segs[seg].hdrs & ICE_FLOW_SEG_HDR_VLAN) ?
 | |
| 		ICE_FLOW_PROT_HDR_SZ_MAC_VLAN : ICE_FLOW_PROT_HDR_SZ_MAC;
 | |
| 
 | |
| 	/* L3 headers */
 | |
| 	if (params->prof->segs[seg].hdrs & ICE_FLOW_SEG_HDR_IPV4)
 | |
| 		sz += ICE_FLOW_PROT_HDR_SZ_IPV4;
 | |
| 	else if (params->prof->segs[seg].hdrs & ICE_FLOW_SEG_HDR_IPV6)
 | |
| 		sz += ICE_FLOW_PROT_HDR_SZ_IPV6;
 | |
| 	else if (params->prof->segs[seg].hdrs & ICE_FLOW_SEG_HDR_ARP)
 | |
| 		sz += ICE_FLOW_PROT_HDR_SZ_ARP;
 | |
| 	else if (params->prof->segs[seg].hdrs & ICE_FLOW_SEG_HDRS_L4_MASK)
 | |
| 		/* An L3 header is required if L4 is specified */
 | |
| 		return 0;
 | |
| 
 | |
| 	/* L4 headers */
 | |
| 	if (params->prof->segs[seg].hdrs & ICE_FLOW_SEG_HDR_ICMP)
 | |
| 		sz += ICE_FLOW_PROT_HDR_SZ_ICMP;
 | |
| 	else if (params->prof->segs[seg].hdrs & ICE_FLOW_SEG_HDR_TCP)
 | |
| 		sz += ICE_FLOW_PROT_HDR_SZ_TCP;
 | |
| 	else if (params->prof->segs[seg].hdrs & ICE_FLOW_SEG_HDR_UDP)
 | |
| 		sz += ICE_FLOW_PROT_HDR_SZ_UDP;
 | |
| 	else if (params->prof->segs[seg].hdrs & ICE_FLOW_SEG_HDR_SCTP)
 | |
| 		sz += ICE_FLOW_PROT_HDR_SZ_SCTP;
 | |
| 
 | |
| 	return sz;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_proc_seg_hdrs - process protocol headers present in pkt segments
 | |
|  * @params: information about the flow to be processed
 | |
|  *
 | |
|  * This function identifies the packet types associated with the protocol
 | |
|  * headers being present in packet segments of the specified flow profile.
 | |
|  */
 | |
| static int ice_flow_proc_seg_hdrs(struct ice_flow_prof_params *params)
 | |
| {
 | |
| 	struct ice_flow_prof *prof;
 | |
| 	u8 i;
 | |
| 
 | |
| 	memset(params->ptypes, 0xff, sizeof(params->ptypes));
 | |
| 
 | |
| 	prof = params->prof;
 | |
| 
 | |
| 	for (i = 0; i < params->prof->segs_cnt; i++) {
 | |
| 		const unsigned long *src;
 | |
| 		u32 hdrs;
 | |
| 
 | |
| 		hdrs = prof->segs[i].hdrs;
 | |
| 
 | |
| 		if (hdrs & ICE_FLOW_SEG_HDR_ETH) {
 | |
| 			src = !i ? (const unsigned long *)ice_ptypes_mac_ofos :
 | |
| 				(const unsigned long *)ice_ptypes_mac_il;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		}
 | |
| 
 | |
| 		if (i && hdrs & ICE_FLOW_SEG_HDR_VLAN) {
 | |
| 			src = (const unsigned long *)ice_ptypes_macvlan_il;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		}
 | |
| 
 | |
| 		if (!i && hdrs & ICE_FLOW_SEG_HDR_ARP) {
 | |
| 			bitmap_and(params->ptypes, params->ptypes,
 | |
| 				   (const unsigned long *)ice_ptypes_arp_of,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		}
 | |
| 
 | |
| 		if ((hdrs & ICE_FLOW_SEG_HDR_IPV4) &&
 | |
| 		    (hdrs & ICE_FLOW_SEG_HDR_IPV_OTHER)) {
 | |
| 			src = i ? (const unsigned long *)ice_ptypes_ipv4_il :
 | |
| 				(const unsigned long *)ice_ptypes_ipv4_ofos_all;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		} else if ((hdrs & ICE_FLOW_SEG_HDR_IPV6) &&
 | |
| 			   (hdrs & ICE_FLOW_SEG_HDR_IPV_OTHER)) {
 | |
| 			src = i ? (const unsigned long *)ice_ptypes_ipv6_il :
 | |
| 				(const unsigned long *)ice_ptypes_ipv6_ofos_all;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		} else if ((hdrs & ICE_FLOW_SEG_HDR_IPV4) &&
 | |
| 			   !(hdrs & ICE_FLOW_SEG_HDRS_L4_MASK_NO_OTHER)) {
 | |
| 			src = !i ? (const unsigned long *)ice_ptypes_ipv4_ofos_no_l4 :
 | |
| 				(const unsigned long *)ice_ptypes_ipv4_il_no_l4;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		} else if (hdrs & ICE_FLOW_SEG_HDR_IPV4) {
 | |
| 			src = !i ? (const unsigned long *)ice_ptypes_ipv4_ofos :
 | |
| 				(const unsigned long *)ice_ptypes_ipv4_il;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		} else if ((hdrs & ICE_FLOW_SEG_HDR_IPV6) &&
 | |
| 			   !(hdrs & ICE_FLOW_SEG_HDRS_L4_MASK_NO_OTHER)) {
 | |
| 			src = !i ? (const unsigned long *)ice_ptypes_ipv6_ofos_no_l4 :
 | |
| 				(const unsigned long *)ice_ptypes_ipv6_il_no_l4;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		} else if (hdrs & ICE_FLOW_SEG_HDR_IPV6) {
 | |
| 			src = !i ? (const unsigned long *)ice_ptypes_ipv6_ofos :
 | |
| 				(const unsigned long *)ice_ptypes_ipv6_il;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		}
 | |
| 
 | |
| 		if (hdrs & ICE_FLOW_SEG_HDR_ETH_NON_IP) {
 | |
| 			src = (const unsigned long *)ice_ptypes_mac_non_ip_ofos;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		} else if (hdrs & ICE_FLOW_SEG_HDR_PPPOE) {
 | |
| 			src = (const unsigned long *)ice_ptypes_pppoe;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		} else {
 | |
| 			src = (const unsigned long *)ice_ptypes_pppoe;
 | |
| 			bitmap_andnot(params->ptypes, params->ptypes, src,
 | |
| 				      ICE_FLOW_PTYPE_MAX);
 | |
| 		}
 | |
| 
 | |
| 		if (hdrs & ICE_FLOW_SEG_HDR_UDP) {
 | |
| 			src = (const unsigned long *)ice_ptypes_udp_il;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		} else if (hdrs & ICE_FLOW_SEG_HDR_TCP) {
 | |
| 			bitmap_and(params->ptypes, params->ptypes,
 | |
| 				   (const unsigned long *)ice_ptypes_tcp_il,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		} else if (hdrs & ICE_FLOW_SEG_HDR_SCTP) {
 | |
| 			src = (const unsigned long *)ice_ptypes_sctp_il;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		}
 | |
| 
 | |
| 		if (hdrs & ICE_FLOW_SEG_HDR_ICMP) {
 | |
| 			src = !i ? (const unsigned long *)ice_ptypes_icmp_of :
 | |
| 				(const unsigned long *)ice_ptypes_icmp_il;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		} else if (hdrs & ICE_FLOW_SEG_HDR_GRE) {
 | |
| 			if (!i) {
 | |
| 				src = (const unsigned long *)ice_ptypes_gre_of;
 | |
| 				bitmap_and(params->ptypes, params->ptypes,
 | |
| 					   src, ICE_FLOW_PTYPE_MAX);
 | |
| 			}
 | |
| 		} else if (hdrs & ICE_FLOW_SEG_HDR_GTPC) {
 | |
| 			src = (const unsigned long *)ice_ptypes_gtpc;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		} else if (hdrs & ICE_FLOW_SEG_HDR_GTPC_TEID) {
 | |
| 			src = (const unsigned long *)ice_ptypes_gtpc_tid;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		} else if (hdrs & ICE_FLOW_SEG_HDR_GTPU_DWN) {
 | |
| 			src = (const unsigned long *)ice_ptypes_gtpu;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 
 | |
| 			/* Attributes for GTP packet with downlink */
 | |
| 			params->attr = ice_attr_gtpu_down;
 | |
| 			params->attr_cnt = ARRAY_SIZE(ice_attr_gtpu_down);
 | |
| 		} else if (hdrs & ICE_FLOW_SEG_HDR_GTPU_UP) {
 | |
| 			src = (const unsigned long *)ice_ptypes_gtpu;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 
 | |
| 			/* Attributes for GTP packet with uplink */
 | |
| 			params->attr = ice_attr_gtpu_up;
 | |
| 			params->attr_cnt = ARRAY_SIZE(ice_attr_gtpu_up);
 | |
| 		} else if (hdrs & ICE_FLOW_SEG_HDR_GTPU_EH) {
 | |
| 			src = (const unsigned long *)ice_ptypes_gtpu;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 
 | |
| 			/* Attributes for GTP packet with Extension Header */
 | |
| 			params->attr = ice_attr_gtpu_eh;
 | |
| 			params->attr_cnt = ARRAY_SIZE(ice_attr_gtpu_eh);
 | |
| 		} else if (hdrs & ICE_FLOW_SEG_HDR_GTPU_IP) {
 | |
| 			src = (const unsigned long *)ice_ptypes_gtpu;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		} else if (hdrs & ICE_FLOW_SEG_HDR_L2TPV3) {
 | |
| 			src = (const unsigned long *)ice_ptypes_l2tpv3;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		} else if (hdrs & ICE_FLOW_SEG_HDR_ESP) {
 | |
| 			src = (const unsigned long *)ice_ptypes_esp;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		} else if (hdrs & ICE_FLOW_SEG_HDR_AH) {
 | |
| 			src = (const unsigned long *)ice_ptypes_ah;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		} else if (hdrs & ICE_FLOW_SEG_HDR_NAT_T_ESP) {
 | |
| 			src = (const unsigned long *)ice_ptypes_nat_t_esp;
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		}
 | |
| 
 | |
| 		if (hdrs & ICE_FLOW_SEG_HDR_PFCP) {
 | |
| 			if (hdrs & ICE_FLOW_SEG_HDR_PFCP_NODE)
 | |
| 				src = (const unsigned long *)ice_ptypes_pfcp_node;
 | |
| 			else
 | |
| 				src = (const unsigned long *)ice_ptypes_pfcp_session;
 | |
| 
 | |
| 			bitmap_and(params->ptypes, params->ptypes, src,
 | |
| 				   ICE_FLOW_PTYPE_MAX);
 | |
| 		} else {
 | |
| 			src = (const unsigned long *)ice_ptypes_pfcp_node;
 | |
| 			bitmap_andnot(params->ptypes, params->ptypes, src,
 | |
| 				      ICE_FLOW_PTYPE_MAX);
 | |
| 
 | |
| 			src = (const unsigned long *)ice_ptypes_pfcp_session;
 | |
| 			bitmap_andnot(params->ptypes, params->ptypes, src,
 | |
| 				      ICE_FLOW_PTYPE_MAX);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_xtract_fld - Create an extraction sequence entry for the given field
 | |
|  * @hw: pointer to the HW struct
 | |
|  * @params: information about the flow to be processed
 | |
|  * @seg: packet segment index of the field to be extracted
 | |
|  * @fld: ID of field to be extracted
 | |
|  * @match: bit field of all fields
 | |
|  *
 | |
|  * This function determines the protocol ID, offset, and size of the given
 | |
|  * field. It then allocates one or more extraction sequence entries for the
 | |
|  * given field, and fill the entries with protocol ID and offset information.
 | |
|  */
 | |
| static int
 | |
| ice_flow_xtract_fld(struct ice_hw *hw, struct ice_flow_prof_params *params,
 | |
| 		    u8 seg, enum ice_flow_field fld, u64 match)
 | |
| {
 | |
| 	enum ice_flow_field sib = ICE_FLOW_FIELD_IDX_MAX;
 | |
| 	enum ice_prot_id prot_id = ICE_PROT_ID_INVAL;
 | |
| 	u8 fv_words = hw->blk[params->blk].es.fvw;
 | |
| 	struct ice_flow_fld_info *flds;
 | |
| 	u16 cnt, ese_bits, i;
 | |
| 	u16 sib_mask = 0;
 | |
| 	u16 mask;
 | |
| 	u16 off;
 | |
| 
 | |
| 	flds = params->prof->segs[seg].fields;
 | |
| 
 | |
| 	switch (fld) {
 | |
| 	case ICE_FLOW_FIELD_IDX_ETH_DA:
 | |
| 	case ICE_FLOW_FIELD_IDX_ETH_SA:
 | |
| 	case ICE_FLOW_FIELD_IDX_S_VLAN:
 | |
| 	case ICE_FLOW_FIELD_IDX_C_VLAN:
 | |
| 		prot_id = seg == 0 ? ICE_PROT_MAC_OF_OR_S : ICE_PROT_MAC_IL;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_ETH_TYPE:
 | |
| 		prot_id = seg == 0 ? ICE_PROT_ETYPE_OL : ICE_PROT_ETYPE_IL;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_IPV4_DSCP:
 | |
| 		prot_id = seg == 0 ? ICE_PROT_IPV4_OF_OR_S : ICE_PROT_IPV4_IL;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_IPV6_DSCP:
 | |
| 		prot_id = seg == 0 ? ICE_PROT_IPV6_OF_OR_S : ICE_PROT_IPV6_IL;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_IPV4_TTL:
 | |
| 	case ICE_FLOW_FIELD_IDX_IPV4_PROT:
 | |
| 		prot_id = seg == 0 ? ICE_PROT_IPV4_OF_OR_S : ICE_PROT_IPV4_IL;
 | |
| 
 | |
| 		/* TTL and PROT share the same extraction seq. entry.
 | |
| 		 * Each is considered a sibling to the other in terms of sharing
 | |
| 		 * the same extraction sequence entry.
 | |
| 		 */
 | |
| 		if (fld == ICE_FLOW_FIELD_IDX_IPV4_TTL)
 | |
| 			sib = ICE_FLOW_FIELD_IDX_IPV4_PROT;
 | |
| 		else if (fld == ICE_FLOW_FIELD_IDX_IPV4_PROT)
 | |
| 			sib = ICE_FLOW_FIELD_IDX_IPV4_TTL;
 | |
| 
 | |
| 		/* If the sibling field is also included, that field's
 | |
| 		 * mask needs to be included.
 | |
| 		 */
 | |
| 		if (match & BIT(sib))
 | |
| 			sib_mask = ice_flds_info[sib].mask;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_IPV6_TTL:
 | |
| 	case ICE_FLOW_FIELD_IDX_IPV6_PROT:
 | |
| 		prot_id = seg == 0 ? ICE_PROT_IPV6_OF_OR_S : ICE_PROT_IPV6_IL;
 | |
| 
 | |
| 		/* TTL and PROT share the same extraction seq. entry.
 | |
| 		 * Each is considered a sibling to the other in terms of sharing
 | |
| 		 * the same extraction sequence entry.
 | |
| 		 */
 | |
| 		if (fld == ICE_FLOW_FIELD_IDX_IPV6_TTL)
 | |
| 			sib = ICE_FLOW_FIELD_IDX_IPV6_PROT;
 | |
| 		else if (fld == ICE_FLOW_FIELD_IDX_IPV6_PROT)
 | |
| 			sib = ICE_FLOW_FIELD_IDX_IPV6_TTL;
 | |
| 
 | |
| 		/* If the sibling field is also included, that field's
 | |
| 		 * mask needs to be included.
 | |
| 		 */
 | |
| 		if (match & BIT(sib))
 | |
| 			sib_mask = ice_flds_info[sib].mask;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_IPV4_SA:
 | |
| 	case ICE_FLOW_FIELD_IDX_IPV4_DA:
 | |
| 		prot_id = seg == 0 ? ICE_PROT_IPV4_OF_OR_S : ICE_PROT_IPV4_IL;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_IPV6_SA:
 | |
| 	case ICE_FLOW_FIELD_IDX_IPV6_DA:
 | |
| 		prot_id = seg == 0 ? ICE_PROT_IPV6_OF_OR_S : ICE_PROT_IPV6_IL;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_TCP_SRC_PORT:
 | |
| 	case ICE_FLOW_FIELD_IDX_TCP_DST_PORT:
 | |
| 	case ICE_FLOW_FIELD_IDX_TCP_FLAGS:
 | |
| 		prot_id = ICE_PROT_TCP_IL;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_UDP_SRC_PORT:
 | |
| 	case ICE_FLOW_FIELD_IDX_UDP_DST_PORT:
 | |
| 		prot_id = ICE_PROT_UDP_IL_OR_S;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT:
 | |
| 	case ICE_FLOW_FIELD_IDX_SCTP_DST_PORT:
 | |
| 		prot_id = ICE_PROT_SCTP_IL;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_GTPC_TEID:
 | |
| 	case ICE_FLOW_FIELD_IDX_GTPU_IP_TEID:
 | |
| 	case ICE_FLOW_FIELD_IDX_GTPU_UP_TEID:
 | |
| 	case ICE_FLOW_FIELD_IDX_GTPU_DWN_TEID:
 | |
| 	case ICE_FLOW_FIELD_IDX_GTPU_EH_TEID:
 | |
| 	case ICE_FLOW_FIELD_IDX_GTPU_EH_QFI:
 | |
| 		/* GTP is accessed through UDP OF protocol */
 | |
| 		prot_id = ICE_PROT_UDP_OF;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_PPPOE_SESS_ID:
 | |
| 		prot_id = ICE_PROT_PPPOE;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_PFCP_SEID:
 | |
| 		prot_id = ICE_PROT_UDP_IL_OR_S;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_L2TPV3_SESS_ID:
 | |
| 		prot_id = ICE_PROT_L2TPV3;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_ESP_SPI:
 | |
| 		prot_id = ICE_PROT_ESP_F;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_AH_SPI:
 | |
| 		prot_id = ICE_PROT_ESP_2;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_NAT_T_ESP_SPI:
 | |
| 		prot_id = ICE_PROT_UDP_IL_OR_S;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_ARP_SIP:
 | |
| 	case ICE_FLOW_FIELD_IDX_ARP_DIP:
 | |
| 	case ICE_FLOW_FIELD_IDX_ARP_SHA:
 | |
| 	case ICE_FLOW_FIELD_IDX_ARP_DHA:
 | |
| 	case ICE_FLOW_FIELD_IDX_ARP_OP:
 | |
| 		prot_id = ICE_PROT_ARP_OF;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_ICMP_TYPE:
 | |
| 	case ICE_FLOW_FIELD_IDX_ICMP_CODE:
 | |
| 		/* ICMP type and code share the same extraction seq. entry */
 | |
| 		prot_id = (params->prof->segs[seg].hdrs & ICE_FLOW_SEG_HDR_IPV4) ?
 | |
| 				ICE_PROT_ICMP_IL : ICE_PROT_ICMPV6_IL;
 | |
| 		sib = fld == ICE_FLOW_FIELD_IDX_ICMP_TYPE ?
 | |
| 			ICE_FLOW_FIELD_IDX_ICMP_CODE :
 | |
| 			ICE_FLOW_FIELD_IDX_ICMP_TYPE;
 | |
| 		break;
 | |
| 	case ICE_FLOW_FIELD_IDX_GRE_KEYID:
 | |
| 		prot_id = ICE_PROT_GRE_OF;
 | |
| 		break;
 | |
| 	default:
 | |
| 		return -EOPNOTSUPP;
 | |
| 	}
 | |
| 
 | |
| 	/* Each extraction sequence entry is a word in size, and extracts a
 | |
| 	 * word-aligned offset from a protocol header.
 | |
| 	 */
 | |
| 	ese_bits = ICE_FLOW_FV_EXTRACT_SZ * BITS_PER_BYTE;
 | |
| 
 | |
| 	flds[fld].xtrct.prot_id = prot_id;
 | |
| 	flds[fld].xtrct.off = (ice_flds_info[fld].off / ese_bits) *
 | |
| 		ICE_FLOW_FV_EXTRACT_SZ;
 | |
| 	flds[fld].xtrct.disp = (u8)(ice_flds_info[fld].off % ese_bits);
 | |
| 	flds[fld].xtrct.idx = params->es_cnt;
 | |
| 	flds[fld].xtrct.mask = ice_flds_info[fld].mask;
 | |
| 
 | |
| 	/* Adjust the next field-entry index after accommodating the number of
 | |
| 	 * entries this field consumes
 | |
| 	 */
 | |
| 	cnt = DIV_ROUND_UP(flds[fld].xtrct.disp + ice_flds_info[fld].size,
 | |
| 			   ese_bits);
 | |
| 
 | |
| 	/* Fill in the extraction sequence entries needed for this field */
 | |
| 	off = flds[fld].xtrct.off;
 | |
| 	mask = flds[fld].xtrct.mask;
 | |
| 	for (i = 0; i < cnt; i++) {
 | |
| 		/* Only consume an extraction sequence entry if there is no
 | |
| 		 * sibling field associated with this field or the sibling entry
 | |
| 		 * already extracts the word shared with this field.
 | |
| 		 */
 | |
| 		if (sib == ICE_FLOW_FIELD_IDX_MAX ||
 | |
| 		    flds[sib].xtrct.prot_id == ICE_PROT_ID_INVAL ||
 | |
| 		    flds[sib].xtrct.off != off) {
 | |
| 			u8 idx;
 | |
| 
 | |
| 			/* Make sure the number of extraction sequence required
 | |
| 			 * does not exceed the block's capability
 | |
| 			 */
 | |
| 			if (params->es_cnt >= fv_words)
 | |
| 				return -ENOSPC;
 | |
| 
 | |
| 			/* some blocks require a reversed field vector layout */
 | |
| 			if (hw->blk[params->blk].es.reverse)
 | |
| 				idx = fv_words - params->es_cnt - 1;
 | |
| 			else
 | |
| 				idx = params->es_cnt;
 | |
| 
 | |
| 			params->es[idx].prot_id = prot_id;
 | |
| 			params->es[idx].off = off;
 | |
| 			params->mask[idx] = mask | sib_mask;
 | |
| 			params->es_cnt++;
 | |
| 		}
 | |
| 
 | |
| 		off += ICE_FLOW_FV_EXTRACT_SZ;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_xtract_raws - Create extract sequence entries for raw bytes
 | |
|  * @hw: pointer to the HW struct
 | |
|  * @params: information about the flow to be processed
 | |
|  * @seg: index of packet segment whose raw fields are to be extracted
 | |
|  */
 | |
| static int
 | |
| ice_flow_xtract_raws(struct ice_hw *hw, struct ice_flow_prof_params *params,
 | |
| 		     u8 seg)
 | |
| {
 | |
| 	u16 fv_words;
 | |
| 	u16 hdrs_sz;
 | |
| 	u8 i;
 | |
| 
 | |
| 	if (!params->prof->segs[seg].raws_cnt)
 | |
| 		return 0;
 | |
| 
 | |
| 	if (params->prof->segs[seg].raws_cnt >
 | |
| 	    ARRAY_SIZE(params->prof->segs[seg].raws))
 | |
| 		return -ENOSPC;
 | |
| 
 | |
| 	/* Offsets within the segment headers are not supported */
 | |
| 	hdrs_sz = ice_flow_calc_seg_sz(params, seg);
 | |
| 	if (!hdrs_sz)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	fv_words = hw->blk[params->blk].es.fvw;
 | |
| 
 | |
| 	for (i = 0; i < params->prof->segs[seg].raws_cnt; i++) {
 | |
| 		struct ice_flow_seg_fld_raw *raw;
 | |
| 		u16 off, cnt, j;
 | |
| 
 | |
| 		raw = ¶ms->prof->segs[seg].raws[i];
 | |
| 
 | |
| 		/* Storing extraction information */
 | |
| 		raw->info.xtrct.prot_id = ICE_PROT_MAC_OF_OR_S;
 | |
| 		raw->info.xtrct.off = (raw->off / ICE_FLOW_FV_EXTRACT_SZ) *
 | |
| 			ICE_FLOW_FV_EXTRACT_SZ;
 | |
| 		raw->info.xtrct.disp = (raw->off % ICE_FLOW_FV_EXTRACT_SZ) *
 | |
| 			BITS_PER_BYTE;
 | |
| 		raw->info.xtrct.idx = params->es_cnt;
 | |
| 
 | |
| 		/* Determine the number of field vector entries this raw field
 | |
| 		 * consumes.
 | |
| 		 */
 | |
| 		cnt = DIV_ROUND_UP(raw->info.xtrct.disp +
 | |
| 				   (raw->info.src.last * BITS_PER_BYTE),
 | |
| 				   (ICE_FLOW_FV_EXTRACT_SZ * BITS_PER_BYTE));
 | |
| 		off = raw->info.xtrct.off;
 | |
| 		for (j = 0; j < cnt; j++) {
 | |
| 			u16 idx;
 | |
| 
 | |
| 			/* Make sure the number of extraction sequence required
 | |
| 			 * does not exceed the block's capability
 | |
| 			 */
 | |
| 			if (params->es_cnt >= hw->blk[params->blk].es.count ||
 | |
| 			    params->es_cnt >= ICE_MAX_FV_WORDS)
 | |
| 				return -ENOSPC;
 | |
| 
 | |
| 			/* some blocks require a reversed field vector layout */
 | |
| 			if (hw->blk[params->blk].es.reverse)
 | |
| 				idx = fv_words - params->es_cnt - 1;
 | |
| 			else
 | |
| 				idx = params->es_cnt;
 | |
| 
 | |
| 			params->es[idx].prot_id = raw->info.xtrct.prot_id;
 | |
| 			params->es[idx].off = off;
 | |
| 			params->es_cnt++;
 | |
| 			off += ICE_FLOW_FV_EXTRACT_SZ;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_create_xtrct_seq - Create an extraction sequence for given segments
 | |
|  * @hw: pointer to the HW struct
 | |
|  * @params: information about the flow to be processed
 | |
|  *
 | |
|  * This function iterates through all matched fields in the given segments, and
 | |
|  * creates an extraction sequence for the fields.
 | |
|  */
 | |
| static int
 | |
| ice_flow_create_xtrct_seq(struct ice_hw *hw,
 | |
| 			  struct ice_flow_prof_params *params)
 | |
| {
 | |
| 	struct ice_flow_prof *prof = params->prof;
 | |
| 	int status = 0;
 | |
| 	u8 i;
 | |
| 
 | |
| 	for (i = 0; i < prof->segs_cnt; i++) {
 | |
| 		u64 match = params->prof->segs[i].match;
 | |
| 		enum ice_flow_field j;
 | |
| 
 | |
| 		for_each_set_bit(j, (unsigned long *)&match,
 | |
| 				 ICE_FLOW_FIELD_IDX_MAX) {
 | |
| 			status = ice_flow_xtract_fld(hw, params, i, j, match);
 | |
| 			if (status)
 | |
| 				return status;
 | |
| 			clear_bit(j, (unsigned long *)&match);
 | |
| 		}
 | |
| 
 | |
| 		/* Process raw matching bytes */
 | |
| 		status = ice_flow_xtract_raws(hw, params, i);
 | |
| 		if (status)
 | |
| 			return status;
 | |
| 	}
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_proc_segs - process all packet segments associated with a profile
 | |
|  * @hw: pointer to the HW struct
 | |
|  * @params: information about the flow to be processed
 | |
|  */
 | |
| static int
 | |
| ice_flow_proc_segs(struct ice_hw *hw, struct ice_flow_prof_params *params)
 | |
| {
 | |
| 	int status;
 | |
| 
 | |
| 	status = ice_flow_proc_seg_hdrs(params);
 | |
| 	if (status)
 | |
| 		return status;
 | |
| 
 | |
| 	status = ice_flow_create_xtrct_seq(hw, params);
 | |
| 	if (status)
 | |
| 		return status;
 | |
| 
 | |
| 	switch (params->blk) {
 | |
| 	case ICE_BLK_FD:
 | |
| 	case ICE_BLK_RSS:
 | |
| 		status = 0;
 | |
| 		break;
 | |
| 	default:
 | |
| 		return -EOPNOTSUPP;
 | |
| 	}
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| #define ICE_FLOW_FIND_PROF_CHK_FLDS	0x00000001
 | |
| #define ICE_FLOW_FIND_PROF_CHK_VSI	0x00000002
 | |
| #define ICE_FLOW_FIND_PROF_NOT_CHK_DIR	0x00000004
 | |
| #define ICE_FLOW_FIND_PROF_CHK_SYMM	0x00000008
 | |
| 
 | |
| /**
 | |
|  * ice_flow_find_prof_conds - Find a profile matching headers and conditions
 | |
|  * @hw: pointer to the HW struct
 | |
|  * @blk: classification stage
 | |
|  * @dir: flow direction
 | |
|  * @segs: array of one or more packet segments that describe the flow
 | |
|  * @segs_cnt: number of packet segments provided
 | |
|  * @symm: symmetric setting for RSS profiles
 | |
|  * @vsi_handle: software VSI handle to check VSI (ICE_FLOW_FIND_PROF_CHK_VSI)
 | |
|  * @conds: additional conditions to be checked (ICE_FLOW_FIND_PROF_CHK_*)
 | |
|  */
 | |
| static struct ice_flow_prof *
 | |
| ice_flow_find_prof_conds(struct ice_hw *hw, enum ice_block blk,
 | |
| 			 enum ice_flow_dir dir, struct ice_flow_seg_info *segs,
 | |
| 			 u8 segs_cnt, bool symm, u16 vsi_handle, u32 conds)
 | |
| {
 | |
| 	struct ice_flow_prof *p, *prof = NULL;
 | |
| 
 | |
| 	mutex_lock(&hw->fl_profs_locks[blk]);
 | |
| 	list_for_each_entry(p, &hw->fl_profs[blk], l_entry)
 | |
| 		if ((p->dir == dir || conds & ICE_FLOW_FIND_PROF_NOT_CHK_DIR) &&
 | |
| 		    segs_cnt && segs_cnt == p->segs_cnt) {
 | |
| 			u8 i;
 | |
| 
 | |
| 			/* Check for profile-VSI association if specified */
 | |
| 			if ((conds & ICE_FLOW_FIND_PROF_CHK_VSI) &&
 | |
| 			    ice_is_vsi_valid(hw, vsi_handle) &&
 | |
| 			    !test_bit(vsi_handle, p->vsis))
 | |
| 				continue;
 | |
| 
 | |
| 			/* Check for symmetric settings */
 | |
| 			if ((conds & ICE_FLOW_FIND_PROF_CHK_SYMM) &&
 | |
| 			    p->symm != symm)
 | |
| 				continue;
 | |
| 
 | |
| 			/* Protocol headers must be checked. Matched fields are
 | |
| 			 * checked if specified.
 | |
| 			 */
 | |
| 			for (i = 0; i < segs_cnt; i++)
 | |
| 				if (segs[i].hdrs != p->segs[i].hdrs ||
 | |
| 				    ((conds & ICE_FLOW_FIND_PROF_CHK_FLDS) &&
 | |
| 				     segs[i].match != p->segs[i].match))
 | |
| 					break;
 | |
| 
 | |
| 			/* A match is found if all segments are matched */
 | |
| 			if (i == segs_cnt) {
 | |
| 				prof = p;
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	mutex_unlock(&hw->fl_profs_locks[blk]);
 | |
| 
 | |
| 	return prof;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_find_prof_id - Look up a profile with given profile ID
 | |
|  * @hw: pointer to the HW struct
 | |
|  * @blk: classification stage
 | |
|  * @prof_id: unique ID to identify this flow profile
 | |
|  */
 | |
| static struct ice_flow_prof *
 | |
| ice_flow_find_prof_id(struct ice_hw *hw, enum ice_block blk, u64 prof_id)
 | |
| {
 | |
| 	struct ice_flow_prof *p;
 | |
| 
 | |
| 	list_for_each_entry(p, &hw->fl_profs[blk], l_entry)
 | |
| 		if (p->id == prof_id)
 | |
| 			return p;
 | |
| 
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_rem_entry_sync - Remove a flow entry
 | |
|  * @hw: pointer to the HW struct
 | |
|  * @blk: classification stage
 | |
|  * @entry: flow entry to be removed
 | |
|  */
 | |
| static int
 | |
| ice_flow_rem_entry_sync(struct ice_hw *hw, enum ice_block __always_unused blk,
 | |
| 			struct ice_flow_entry *entry)
 | |
| {
 | |
| 	if (!entry)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	list_del(&entry->l_entry);
 | |
| 
 | |
| 	devm_kfree(ice_hw_to_dev(hw), entry);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_add_prof_sync - Add a flow profile for packet segments and fields
 | |
|  * @hw: pointer to the HW struct
 | |
|  * @blk: classification stage
 | |
|  * @dir: flow direction
 | |
|  * @segs: array of one or more packet segments that describe the flow
 | |
|  * @segs_cnt: number of packet segments provided
 | |
|  * @symm: symmetric setting for RSS profiles
 | |
|  * @prof: stores the returned flow profile added
 | |
|  *
 | |
|  * Assumption: the caller has acquired the lock to the profile list
 | |
|  */
 | |
| static int
 | |
| ice_flow_add_prof_sync(struct ice_hw *hw, enum ice_block blk,
 | |
| 		       enum ice_flow_dir dir,
 | |
| 		       struct ice_flow_seg_info *segs, u8 segs_cnt,
 | |
| 		       bool symm, struct ice_flow_prof **prof)
 | |
| {
 | |
| 	struct ice_flow_prof_params *params;
 | |
| 	struct ice_prof_id *ids;
 | |
| 	int status;
 | |
| 	u64 prof_id;
 | |
| 	u8 i;
 | |
| 
 | |
| 	if (!prof)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	ids = &hw->blk[blk].prof_id;
 | |
| 	prof_id = find_first_zero_bit(ids->id, ids->count);
 | |
| 	if (prof_id >= ids->count)
 | |
| 		return -ENOSPC;
 | |
| 
 | |
| 	params = kzalloc(sizeof(*params), GFP_KERNEL);
 | |
| 	if (!params)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	params->prof = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*params->prof),
 | |
| 				    GFP_KERNEL);
 | |
| 	if (!params->prof) {
 | |
| 		status = -ENOMEM;
 | |
| 		goto free_params;
 | |
| 	}
 | |
| 
 | |
| 	/* initialize extraction sequence to all invalid (0xff) */
 | |
| 	for (i = 0; i < ICE_MAX_FV_WORDS; i++) {
 | |
| 		params->es[i].prot_id = ICE_PROT_INVALID;
 | |
| 		params->es[i].off = ICE_FV_OFFSET_INVAL;
 | |
| 	}
 | |
| 
 | |
| 	params->blk = blk;
 | |
| 	params->prof->id = prof_id;
 | |
| 	params->prof->dir = dir;
 | |
| 	params->prof->segs_cnt = segs_cnt;
 | |
| 	params->prof->symm = symm;
 | |
| 
 | |
| 	/* Make a copy of the segments that need to be persistent in the flow
 | |
| 	 * profile instance
 | |
| 	 */
 | |
| 	for (i = 0; i < segs_cnt; i++)
 | |
| 		memcpy(¶ms->prof->segs[i], &segs[i], sizeof(*segs));
 | |
| 
 | |
| 	status = ice_flow_proc_segs(hw, params);
 | |
| 	if (status) {
 | |
| 		ice_debug(hw, ICE_DBG_FLOW, "Error processing a flow's packet segments\n");
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	/* Add a HW profile for this flow profile */
 | |
| 	status = ice_add_prof(hw, blk, prof_id, (u8 *)params->ptypes,
 | |
| 			      params->attr, params->attr_cnt, params->es,
 | |
| 			      params->mask, symm, true);
 | |
| 	if (status) {
 | |
| 		ice_debug(hw, ICE_DBG_FLOW, "Error adding a HW flow profile\n");
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	INIT_LIST_HEAD(¶ms->prof->entries);
 | |
| 	mutex_init(¶ms->prof->entries_lock);
 | |
| 	set_bit(prof_id, ids->id);
 | |
| 	*prof = params->prof;
 | |
| 
 | |
| out:
 | |
| 	if (status)
 | |
| 		devm_kfree(ice_hw_to_dev(hw), params->prof);
 | |
| free_params:
 | |
| 	kfree(params);
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_rem_prof_sync - remove a flow profile
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @blk: classification stage
 | |
|  * @prof: pointer to flow profile to remove
 | |
|  *
 | |
|  * Assumption: the caller has acquired the lock to the profile list
 | |
|  */
 | |
| static int
 | |
| ice_flow_rem_prof_sync(struct ice_hw *hw, enum ice_block blk,
 | |
| 		       struct ice_flow_prof *prof)
 | |
| {
 | |
| 	int status;
 | |
| 
 | |
| 	/* Remove all remaining flow entries before removing the flow profile */
 | |
| 	if (!list_empty(&prof->entries)) {
 | |
| 		struct ice_flow_entry *e, *t;
 | |
| 
 | |
| 		mutex_lock(&prof->entries_lock);
 | |
| 
 | |
| 		list_for_each_entry_safe(e, t, &prof->entries, l_entry) {
 | |
| 			status = ice_flow_rem_entry_sync(hw, blk, e);
 | |
| 			if (status)
 | |
| 				break;
 | |
| 		}
 | |
| 
 | |
| 		mutex_unlock(&prof->entries_lock);
 | |
| 	}
 | |
| 
 | |
| 	/* Remove all hardware profiles associated with this flow profile */
 | |
| 	status = ice_rem_prof(hw, blk, prof->id);
 | |
| 	if (!status) {
 | |
| 		clear_bit(prof->id, hw->blk[blk].prof_id.id);
 | |
| 		list_del(&prof->l_entry);
 | |
| 		mutex_destroy(&prof->entries_lock);
 | |
| 		devm_kfree(ice_hw_to_dev(hw), prof);
 | |
| 	}
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_assoc_prof - associate a VSI with a flow profile
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @blk: classification stage
 | |
|  * @prof: pointer to flow profile
 | |
|  * @vsi_handle: software VSI handle
 | |
|  *
 | |
|  * Assumption: the caller has acquired the lock to the profile list
 | |
|  * and the software VSI handle has been validated
 | |
|  */
 | |
| static int
 | |
| ice_flow_assoc_prof(struct ice_hw *hw, enum ice_block blk,
 | |
| 		    struct ice_flow_prof *prof, u16 vsi_handle)
 | |
| {
 | |
| 	int status = 0;
 | |
| 
 | |
| 	if (!test_bit(vsi_handle, prof->vsis)) {
 | |
| 		status = ice_add_prof_id_flow(hw, blk,
 | |
| 					      ice_get_hw_vsi_num(hw,
 | |
| 								 vsi_handle),
 | |
| 					      prof->id);
 | |
| 		if (!status)
 | |
| 			set_bit(vsi_handle, prof->vsis);
 | |
| 		else
 | |
| 			ice_debug(hw, ICE_DBG_FLOW, "HW profile add failed, %d\n",
 | |
| 				  status);
 | |
| 	}
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_disassoc_prof - disassociate a VSI from a flow profile
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @blk: classification stage
 | |
|  * @prof: pointer to flow profile
 | |
|  * @vsi_handle: software VSI handle
 | |
|  *
 | |
|  * Assumption: the caller has acquired the lock to the profile list
 | |
|  * and the software VSI handle has been validated
 | |
|  */
 | |
| static int
 | |
| ice_flow_disassoc_prof(struct ice_hw *hw, enum ice_block blk,
 | |
| 		       struct ice_flow_prof *prof, u16 vsi_handle)
 | |
| {
 | |
| 	int status = 0;
 | |
| 
 | |
| 	if (test_bit(vsi_handle, prof->vsis)) {
 | |
| 		status = ice_rem_prof_id_flow(hw, blk,
 | |
| 					      ice_get_hw_vsi_num(hw,
 | |
| 								 vsi_handle),
 | |
| 					      prof->id);
 | |
| 		if (!status)
 | |
| 			clear_bit(vsi_handle, prof->vsis);
 | |
| 		else
 | |
| 			ice_debug(hw, ICE_DBG_FLOW, "HW profile remove failed, %d\n",
 | |
| 				  status);
 | |
| 	}
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| #define FLAG_GTP_EH_PDU_LINK	BIT_ULL(13)
 | |
| #define FLAG_GTP_EH_PDU		BIT_ULL(14)
 | |
| 
 | |
| #define HI_BYTE_IN_WORD		GENMASK(15, 8)
 | |
| #define LO_BYTE_IN_WORD		GENMASK(7, 0)
 | |
| 
 | |
| #define FLAG_GTPU_MSK	\
 | |
| 	(FLAG_GTP_EH_PDU | FLAG_GTP_EH_PDU_LINK)
 | |
| #define FLAG_GTPU_UP	\
 | |
| 	(FLAG_GTP_EH_PDU | FLAG_GTP_EH_PDU_LINK)
 | |
| #define FLAG_GTPU_DW	FLAG_GTP_EH_PDU
 | |
| 
 | |
| /**
 | |
|  * ice_flow_set_parser_prof - Set flow profile based on the parsed profile info
 | |
|  * @hw: pointer to the HW struct
 | |
|  * @dest_vsi: dest VSI
 | |
|  * @fdir_vsi: fdir programming VSI
 | |
|  * @prof: stores parsed profile info from raw flow
 | |
|  * @blk: classification blk
 | |
|  *
 | |
|  * Return: 0 on success or negative errno on failure.
 | |
|  */
 | |
| int
 | |
| ice_flow_set_parser_prof(struct ice_hw *hw, u16 dest_vsi, u16 fdir_vsi,
 | |
| 			 struct ice_parser_profile *prof, enum ice_block blk)
 | |
| {
 | |
| 	u64 id = find_first_bit(prof->ptypes, ICE_FLOW_PTYPE_MAX);
 | |
| 	struct ice_flow_prof_params *params __free(kfree);
 | |
| 	u8 fv_words = hw->blk[blk].es.fvw;
 | |
| 	int status;
 | |
| 	int i, idx;
 | |
| 
 | |
| 	params = kzalloc(sizeof(*params), GFP_KERNEL);
 | |
| 	if (!params)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	for (i = 0; i < ICE_MAX_FV_WORDS; i++) {
 | |
| 		params->es[i].prot_id = ICE_PROT_INVALID;
 | |
| 		params->es[i].off = ICE_FV_OFFSET_INVAL;
 | |
| 	}
 | |
| 
 | |
| 	for (i = 0; i < prof->fv_num; i++) {
 | |
| 		if (hw->blk[blk].es.reverse)
 | |
| 			idx = fv_words - i - 1;
 | |
| 		else
 | |
| 			idx = i;
 | |
| 		params->es[idx].prot_id = prof->fv[i].proto_id;
 | |
| 		params->es[idx].off = prof->fv[i].offset;
 | |
| 		params->mask[idx] = (((prof->fv[i].msk) << BITS_PER_BYTE) &
 | |
| 				      HI_BYTE_IN_WORD) |
 | |
| 				    (((prof->fv[i].msk) >> BITS_PER_BYTE) &
 | |
| 				      LO_BYTE_IN_WORD);
 | |
| 	}
 | |
| 
 | |
| 	switch (prof->flags) {
 | |
| 	case FLAG_GTPU_DW:
 | |
| 		params->attr = ice_attr_gtpu_down;
 | |
| 		params->attr_cnt = ARRAY_SIZE(ice_attr_gtpu_down);
 | |
| 		break;
 | |
| 	case FLAG_GTPU_UP:
 | |
| 		params->attr = ice_attr_gtpu_up;
 | |
| 		params->attr_cnt = ARRAY_SIZE(ice_attr_gtpu_up);
 | |
| 		break;
 | |
| 	default:
 | |
| 		if (prof->flags_msk & FLAG_GTPU_MSK) {
 | |
| 			params->attr = ice_attr_gtpu_session;
 | |
| 			params->attr_cnt = ARRAY_SIZE(ice_attr_gtpu_session);
 | |
| 		}
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	status = ice_add_prof(hw, blk, id, (u8 *)prof->ptypes,
 | |
| 			      params->attr, params->attr_cnt,
 | |
| 			      params->es, params->mask, false, false);
 | |
| 	if (status)
 | |
| 		return status;
 | |
| 
 | |
| 	status = ice_flow_assoc_fdir_prof(hw, blk, dest_vsi, fdir_vsi, id);
 | |
| 	if (status)
 | |
| 		ice_rem_prof(hw, blk, id);
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_add_prof - Add a flow profile for packet segments and matched fields
 | |
|  * @hw: pointer to the HW struct
 | |
|  * @blk: classification stage
 | |
|  * @dir: flow direction
 | |
|  * @segs: array of one or more packet segments that describe the flow
 | |
|  * @segs_cnt: number of packet segments provided
 | |
|  * @symm: symmetric setting for RSS profiles
 | |
|  * @prof: stores the returned flow profile added
 | |
|  */
 | |
| int
 | |
| ice_flow_add_prof(struct ice_hw *hw, enum ice_block blk, enum ice_flow_dir dir,
 | |
| 		  struct ice_flow_seg_info *segs, u8 segs_cnt,
 | |
| 		  bool symm, struct ice_flow_prof **prof)
 | |
| {
 | |
| 	int status;
 | |
| 
 | |
| 	if (segs_cnt > ICE_FLOW_SEG_MAX)
 | |
| 		return -ENOSPC;
 | |
| 
 | |
| 	if (!segs_cnt)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	if (!segs)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	status = ice_flow_val_hdrs(segs, segs_cnt);
 | |
| 	if (status)
 | |
| 		return status;
 | |
| 
 | |
| 	mutex_lock(&hw->fl_profs_locks[blk]);
 | |
| 
 | |
| 	status = ice_flow_add_prof_sync(hw, blk, dir, segs, segs_cnt,
 | |
| 					symm, prof);
 | |
| 	if (!status)
 | |
| 		list_add(&(*prof)->l_entry, &hw->fl_profs[blk]);
 | |
| 
 | |
| 	mutex_unlock(&hw->fl_profs_locks[blk]);
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_rem_prof - Remove a flow profile and all entries associated with it
 | |
|  * @hw: pointer to the HW struct
 | |
|  * @blk: the block for which the flow profile is to be removed
 | |
|  * @prof_id: unique ID of the flow profile to be removed
 | |
|  */
 | |
| int ice_flow_rem_prof(struct ice_hw *hw, enum ice_block blk, u64 prof_id)
 | |
| {
 | |
| 	struct ice_flow_prof *prof;
 | |
| 	int status;
 | |
| 
 | |
| 	mutex_lock(&hw->fl_profs_locks[blk]);
 | |
| 
 | |
| 	prof = ice_flow_find_prof_id(hw, blk, prof_id);
 | |
| 	if (!prof) {
 | |
| 		status = -ENOENT;
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	/* prof becomes invalid after the call */
 | |
| 	status = ice_flow_rem_prof_sync(hw, blk, prof);
 | |
| 
 | |
| out:
 | |
| 	mutex_unlock(&hw->fl_profs_locks[blk]);
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_add_entry - Add a flow entry
 | |
|  * @hw: pointer to the HW struct
 | |
|  * @blk: classification stage
 | |
|  * @prof_id: ID of the profile to add a new flow entry to
 | |
|  * @entry_id: unique ID to identify this flow entry
 | |
|  * @vsi_handle: software VSI handle for the flow entry
 | |
|  * @prio: priority of the flow entry
 | |
|  * @data: pointer to a data buffer containing flow entry's match values/masks
 | |
|  * @entry_h: pointer to buffer that receives the new flow entry's handle
 | |
|  */
 | |
| int
 | |
| ice_flow_add_entry(struct ice_hw *hw, enum ice_block blk, u64 prof_id,
 | |
| 		   u64 entry_id, u16 vsi_handle, enum ice_flow_priority prio,
 | |
| 		   void *data, u64 *entry_h)
 | |
| {
 | |
| 	struct ice_flow_entry *e = NULL;
 | |
| 	struct ice_flow_prof *prof;
 | |
| 	int status;
 | |
| 
 | |
| 	/* No flow entry data is expected for RSS */
 | |
| 	if (!entry_h || (!data && blk != ICE_BLK_RSS))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	if (!ice_is_vsi_valid(hw, vsi_handle))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	mutex_lock(&hw->fl_profs_locks[blk]);
 | |
| 
 | |
| 	prof = ice_flow_find_prof_id(hw, blk, prof_id);
 | |
| 	if (!prof) {
 | |
| 		status = -ENOENT;
 | |
| 	} else {
 | |
| 		/* Allocate memory for the entry being added and associate
 | |
| 		 * the VSI to the found flow profile
 | |
| 		 */
 | |
| 		e = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*e), GFP_KERNEL);
 | |
| 		if (!e)
 | |
| 			status = -ENOMEM;
 | |
| 		else
 | |
| 			status = ice_flow_assoc_prof(hw, blk, prof, vsi_handle);
 | |
| 	}
 | |
| 
 | |
| 	mutex_unlock(&hw->fl_profs_locks[blk]);
 | |
| 	if (status)
 | |
| 		goto out;
 | |
| 
 | |
| 	e->id = entry_id;
 | |
| 	e->vsi_handle = vsi_handle;
 | |
| 	e->prof = prof;
 | |
| 	e->priority = prio;
 | |
| 
 | |
| 	switch (blk) {
 | |
| 	case ICE_BLK_FD:
 | |
| 	case ICE_BLK_RSS:
 | |
| 		break;
 | |
| 	default:
 | |
| 		status = -EOPNOTSUPP;
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	mutex_lock(&prof->entries_lock);
 | |
| 	list_add(&e->l_entry, &prof->entries);
 | |
| 	mutex_unlock(&prof->entries_lock);
 | |
| 
 | |
| 	*entry_h = ICE_FLOW_ENTRY_HNDL(e);
 | |
| 
 | |
| out:
 | |
| 	if (status)
 | |
| 		devm_kfree(ice_hw_to_dev(hw), e);
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_rem_entry - Remove a flow entry
 | |
|  * @hw: pointer to the HW struct
 | |
|  * @blk: classification stage
 | |
|  * @entry_h: handle to the flow entry to be removed
 | |
|  */
 | |
| int ice_flow_rem_entry(struct ice_hw *hw, enum ice_block blk, u64 entry_h)
 | |
| {
 | |
| 	struct ice_flow_entry *entry;
 | |
| 	struct ice_flow_prof *prof;
 | |
| 	int status = 0;
 | |
| 
 | |
| 	if (entry_h == ICE_FLOW_ENTRY_HANDLE_INVAL)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	entry = ICE_FLOW_ENTRY_PTR(entry_h);
 | |
| 
 | |
| 	/* Retain the pointer to the flow profile as the entry will be freed */
 | |
| 	prof = entry->prof;
 | |
| 
 | |
| 	if (prof) {
 | |
| 		mutex_lock(&prof->entries_lock);
 | |
| 		status = ice_flow_rem_entry_sync(hw, blk, entry);
 | |
| 		mutex_unlock(&prof->entries_lock);
 | |
| 	}
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_set_fld_ext - specifies locations of field from entry's input buffer
 | |
|  * @seg: packet segment the field being set belongs to
 | |
|  * @fld: field to be set
 | |
|  * @field_type: type of the field
 | |
|  * @val_loc: if not ICE_FLOW_FLD_OFF_INVAL, location of the value to match from
 | |
|  *           entry's input buffer
 | |
|  * @mask_loc: if not ICE_FLOW_FLD_OFF_INVAL, location of mask value from entry's
 | |
|  *            input buffer
 | |
|  * @last_loc: if not ICE_FLOW_FLD_OFF_INVAL, location of last/upper value from
 | |
|  *            entry's input buffer
 | |
|  *
 | |
|  * This helper function stores information of a field being matched, including
 | |
|  * the type of the field and the locations of the value to match, the mask, and
 | |
|  * the upper-bound value in the start of the input buffer for a flow entry.
 | |
|  * This function should only be used for fixed-size data structures.
 | |
|  *
 | |
|  * This function also opportunistically determines the protocol headers to be
 | |
|  * present based on the fields being set. Some fields cannot be used alone to
 | |
|  * determine the protocol headers present. Sometimes, fields for particular
 | |
|  * protocol headers are not matched. In those cases, the protocol headers
 | |
|  * must be explicitly set.
 | |
|  */
 | |
| static void
 | |
| ice_flow_set_fld_ext(struct ice_flow_seg_info *seg, enum ice_flow_field fld,
 | |
| 		     enum ice_flow_fld_match_type field_type, u16 val_loc,
 | |
| 		     u16 mask_loc, u16 last_loc)
 | |
| {
 | |
| 	u64 bit = BIT_ULL(fld);
 | |
| 
 | |
| 	seg->match |= bit;
 | |
| 	if (field_type == ICE_FLOW_FLD_TYPE_RANGE)
 | |
| 		seg->range |= bit;
 | |
| 
 | |
| 	seg->fields[fld].type = field_type;
 | |
| 	seg->fields[fld].src.val = val_loc;
 | |
| 	seg->fields[fld].src.mask = mask_loc;
 | |
| 	seg->fields[fld].src.last = last_loc;
 | |
| 
 | |
| 	ICE_FLOW_SET_HDRS(seg, ice_flds_info[fld].hdr);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_set_fld - specifies locations of field from entry's input buffer
 | |
|  * @seg: packet segment the field being set belongs to
 | |
|  * @fld: field to be set
 | |
|  * @val_loc: if not ICE_FLOW_FLD_OFF_INVAL, location of the value to match from
 | |
|  *           entry's input buffer
 | |
|  * @mask_loc: if not ICE_FLOW_FLD_OFF_INVAL, location of mask value from entry's
 | |
|  *            input buffer
 | |
|  * @last_loc: if not ICE_FLOW_FLD_OFF_INVAL, location of last/upper value from
 | |
|  *            entry's input buffer
 | |
|  * @range: indicate if field being matched is to be in a range
 | |
|  *
 | |
|  * This function specifies the locations, in the form of byte offsets from the
 | |
|  * start of the input buffer for a flow entry, from where the value to match,
 | |
|  * the mask value, and upper value can be extracted. These locations are then
 | |
|  * stored in the flow profile. When adding a flow entry associated with the
 | |
|  * flow profile, these locations will be used to quickly extract the values and
 | |
|  * create the content of a match entry. This function should only be used for
 | |
|  * fixed-size data structures.
 | |
|  */
 | |
| void
 | |
| ice_flow_set_fld(struct ice_flow_seg_info *seg, enum ice_flow_field fld,
 | |
| 		 u16 val_loc, u16 mask_loc, u16 last_loc, bool range)
 | |
| {
 | |
| 	enum ice_flow_fld_match_type t = range ?
 | |
| 		ICE_FLOW_FLD_TYPE_RANGE : ICE_FLOW_FLD_TYPE_REG;
 | |
| 
 | |
| 	ice_flow_set_fld_ext(seg, fld, t, val_loc, mask_loc, last_loc);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_add_fld_raw - sets locations of a raw field from entry's input buf
 | |
|  * @seg: packet segment the field being set belongs to
 | |
|  * @off: offset of the raw field from the beginning of the segment in bytes
 | |
|  * @len: length of the raw pattern to be matched
 | |
|  * @val_loc: location of the value to match from entry's input buffer
 | |
|  * @mask_loc: location of mask value from entry's input buffer
 | |
|  *
 | |
|  * This function specifies the offset of the raw field to be match from the
 | |
|  * beginning of the specified packet segment, and the locations, in the form of
 | |
|  * byte offsets from the start of the input buffer for a flow entry, from where
 | |
|  * the value to match and the mask value to be extracted. These locations are
 | |
|  * then stored in the flow profile. When adding flow entries to the associated
 | |
|  * flow profile, these locations can be used to quickly extract the values to
 | |
|  * create the content of a match entry. This function should only be used for
 | |
|  * fixed-size data structures.
 | |
|  */
 | |
| void
 | |
| ice_flow_add_fld_raw(struct ice_flow_seg_info *seg, u16 off, u8 len,
 | |
| 		     u16 val_loc, u16 mask_loc)
 | |
| {
 | |
| 	if (seg->raws_cnt < ICE_FLOW_SEG_RAW_FLD_MAX) {
 | |
| 		seg->raws[seg->raws_cnt].off = off;
 | |
| 		seg->raws[seg->raws_cnt].info.type = ICE_FLOW_FLD_TYPE_SIZE;
 | |
| 		seg->raws[seg->raws_cnt].info.src.val = val_loc;
 | |
| 		seg->raws[seg->raws_cnt].info.src.mask = mask_loc;
 | |
| 		/* The "last" field is used to store the length of the field */
 | |
| 		seg->raws[seg->raws_cnt].info.src.last = len;
 | |
| 	}
 | |
| 
 | |
| 	/* Overflows of "raws" will be handled as an error condition later in
 | |
| 	 * the flow when this information is processed.
 | |
| 	 */
 | |
| 	seg->raws_cnt++;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_flow_rem_vsi_prof - remove VSI from flow profile
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @vsi_handle: software VSI handle
 | |
|  * @prof_id: unique ID to identify this flow profile
 | |
|  *
 | |
|  * This function removes the flow entries associated to the input
 | |
|  * VSI handle and disassociate the VSI from the flow profile.
 | |
|  */
 | |
| int ice_flow_rem_vsi_prof(struct ice_hw *hw, u16 vsi_handle, u64 prof_id)
 | |
| {
 | |
| 	struct ice_flow_prof *prof;
 | |
| 	int status = 0;
 | |
| 
 | |
| 	if (!ice_is_vsi_valid(hw, vsi_handle))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	/* find flow profile pointer with input package block and profile ID */
 | |
| 	prof = ice_flow_find_prof_id(hw, ICE_BLK_FD, prof_id);
 | |
| 	if (!prof) {
 | |
| 		ice_debug(hw, ICE_DBG_PKG, "Cannot find flow profile id=%llu\n",
 | |
| 			  prof_id);
 | |
| 		return -ENOENT;
 | |
| 	}
 | |
| 
 | |
| 	/* Remove all remaining flow entries before removing the flow profile */
 | |
| 	if (!list_empty(&prof->entries)) {
 | |
| 		struct ice_flow_entry *e, *t;
 | |
| 
 | |
| 		mutex_lock(&prof->entries_lock);
 | |
| 		list_for_each_entry_safe(e, t, &prof->entries, l_entry) {
 | |
| 			if (e->vsi_handle != vsi_handle)
 | |
| 				continue;
 | |
| 
 | |
| 			status = ice_flow_rem_entry_sync(hw, ICE_BLK_FD, e);
 | |
| 			if (status)
 | |
| 				break;
 | |
| 		}
 | |
| 		mutex_unlock(&prof->entries_lock);
 | |
| 	}
 | |
| 	if (status)
 | |
| 		return status;
 | |
| 
 | |
| 	/* disassociate the flow profile from sw VSI handle */
 | |
| 	status = ice_flow_disassoc_prof(hw, ICE_BLK_FD, prof, vsi_handle);
 | |
| 	if (status)
 | |
| 		ice_debug(hw, ICE_DBG_PKG, "ice_flow_disassoc_prof() failed with status=%d\n",
 | |
| 			  status);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| #define ICE_FLOW_RSS_SEG_HDR_L2_MASKS \
 | |
| 	(ICE_FLOW_SEG_HDR_ETH | ICE_FLOW_SEG_HDR_VLAN)
 | |
| 
 | |
| #define ICE_FLOW_RSS_SEG_HDR_L3_MASKS \
 | |
| 	(ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV6)
 | |
| 
 | |
| #define ICE_FLOW_RSS_SEG_HDR_L4_MASKS \
 | |
| 	(ICE_FLOW_SEG_HDR_TCP | ICE_FLOW_SEG_HDR_UDP | ICE_FLOW_SEG_HDR_SCTP)
 | |
| 
 | |
| #define ICE_FLOW_RSS_SEG_HDR_VAL_MASKS \
 | |
| 	(ICE_FLOW_RSS_SEG_HDR_L2_MASKS | \
 | |
| 	 ICE_FLOW_RSS_SEG_HDR_L3_MASKS | \
 | |
| 	 ICE_FLOW_RSS_SEG_HDR_L4_MASKS)
 | |
| 
 | |
| /**
 | |
|  * ice_flow_set_rss_seg_info - setup packet segments for RSS
 | |
|  * @segs: pointer to the flow field segment(s)
 | |
|  * @seg_cnt: segment count
 | |
|  * @cfg: configure parameters
 | |
|  *
 | |
|  * Helper function to extract fields from hash bitmap and use flow
 | |
|  * header value to set flow field segment for further use in flow
 | |
|  * profile entry or removal.
 | |
|  */
 | |
| static int
 | |
| ice_flow_set_rss_seg_info(struct ice_flow_seg_info *segs, u8 seg_cnt,
 | |
| 			  const struct ice_rss_hash_cfg *cfg)
 | |
| {
 | |
| 	struct ice_flow_seg_info *seg;
 | |
| 	u64 val;
 | |
| 	u16 i;
 | |
| 
 | |
| 	/* set inner most segment */
 | |
| 	seg = &segs[seg_cnt - 1];
 | |
| 
 | |
| 	for_each_set_bit(i, (const unsigned long *)&cfg->hash_flds,
 | |
| 			 (u16)ICE_FLOW_FIELD_IDX_MAX)
 | |
| 		ice_flow_set_fld(seg, (enum ice_flow_field)i,
 | |
| 				 ICE_FLOW_FLD_OFF_INVAL, ICE_FLOW_FLD_OFF_INVAL,
 | |
| 				 ICE_FLOW_FLD_OFF_INVAL, false);
 | |
| 
 | |
| 	ICE_FLOW_SET_HDRS(seg, cfg->addl_hdrs);
 | |
| 
 | |
| 	/* set outer most header */
 | |
| 	if (cfg->hdr_type == ICE_RSS_INNER_HEADERS_W_OUTER_IPV4)
 | |
| 		segs[ICE_RSS_OUTER_HEADERS].hdrs |= ICE_FLOW_SEG_HDR_IPV4 |
 | |
| 						    ICE_FLOW_SEG_HDR_IPV_OTHER;
 | |
| 	else if (cfg->hdr_type == ICE_RSS_INNER_HEADERS_W_OUTER_IPV6)
 | |
| 		segs[ICE_RSS_OUTER_HEADERS].hdrs |= ICE_FLOW_SEG_HDR_IPV6 |
 | |
| 						    ICE_FLOW_SEG_HDR_IPV_OTHER;
 | |
| 
 | |
| 	if (seg->hdrs & ~ICE_FLOW_RSS_SEG_HDR_VAL_MASKS &
 | |
| 	    ~ICE_FLOW_RSS_HDRS_INNER_MASK & ~ICE_FLOW_SEG_HDR_IPV_OTHER)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	val = (u64)(seg->hdrs & ICE_FLOW_RSS_SEG_HDR_L3_MASKS);
 | |
| 	if (val && !is_power_of_2(val))
 | |
| 		return -EIO;
 | |
| 
 | |
| 	val = (u64)(seg->hdrs & ICE_FLOW_RSS_SEG_HDR_L4_MASKS);
 | |
| 	if (val && !is_power_of_2(val))
 | |
| 		return -EIO;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_rem_vsi_rss_list - remove VSI from RSS list
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @vsi_handle: software VSI handle
 | |
|  *
 | |
|  * Remove the VSI from all RSS configurations in the list.
 | |
|  */
 | |
| void ice_rem_vsi_rss_list(struct ice_hw *hw, u16 vsi_handle)
 | |
| {
 | |
| 	struct ice_rss_cfg *r, *tmp;
 | |
| 
 | |
| 	if (list_empty(&hw->rss_list_head))
 | |
| 		return;
 | |
| 
 | |
| 	mutex_lock(&hw->rss_locks);
 | |
| 	list_for_each_entry_safe(r, tmp, &hw->rss_list_head, l_entry)
 | |
| 		if (test_and_clear_bit(vsi_handle, r->vsis))
 | |
| 			if (bitmap_empty(r->vsis, ICE_MAX_VSI)) {
 | |
| 				list_del(&r->l_entry);
 | |
| 				devm_kfree(ice_hw_to_dev(hw), r);
 | |
| 			}
 | |
| 	mutex_unlock(&hw->rss_locks);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_rem_vsi_rss_cfg - remove RSS configurations associated with VSI
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @vsi_handle: software VSI handle
 | |
|  *
 | |
|  * This function will iterate through all flow profiles and disassociate
 | |
|  * the VSI from that profile. If the flow profile has no VSIs it will
 | |
|  * be removed.
 | |
|  */
 | |
| int ice_rem_vsi_rss_cfg(struct ice_hw *hw, u16 vsi_handle)
 | |
| {
 | |
| 	const enum ice_block blk = ICE_BLK_RSS;
 | |
| 	struct ice_flow_prof *p, *t;
 | |
| 	int status = 0;
 | |
| 
 | |
| 	if (!ice_is_vsi_valid(hw, vsi_handle))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	if (list_empty(&hw->fl_profs[blk]))
 | |
| 		return 0;
 | |
| 
 | |
| 	mutex_lock(&hw->rss_locks);
 | |
| 	list_for_each_entry_safe(p, t, &hw->fl_profs[blk], l_entry)
 | |
| 		if (test_bit(vsi_handle, p->vsis)) {
 | |
| 			status = ice_flow_disassoc_prof(hw, blk, p, vsi_handle);
 | |
| 			if (status)
 | |
| 				break;
 | |
| 
 | |
| 			if (bitmap_empty(p->vsis, ICE_MAX_VSI)) {
 | |
| 				status = ice_flow_rem_prof(hw, blk, p->id);
 | |
| 				if (status)
 | |
| 					break;
 | |
| 			}
 | |
| 		}
 | |
| 	mutex_unlock(&hw->rss_locks);
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_get_rss_hdr_type - get a RSS profile's header type
 | |
|  * @prof: RSS flow profile
 | |
|  */
 | |
| static enum ice_rss_cfg_hdr_type
 | |
| ice_get_rss_hdr_type(struct ice_flow_prof *prof)
 | |
| {
 | |
| 	if (prof->segs_cnt == ICE_FLOW_SEG_SINGLE) {
 | |
| 		return ICE_RSS_OUTER_HEADERS;
 | |
| 	} else if (prof->segs_cnt == ICE_FLOW_SEG_MAX) {
 | |
| 		const struct ice_flow_seg_info *s;
 | |
| 
 | |
| 		s = &prof->segs[ICE_RSS_OUTER_HEADERS];
 | |
| 		if (s->hdrs == ICE_FLOW_SEG_HDR_NONE)
 | |
| 			return ICE_RSS_INNER_HEADERS;
 | |
| 		if (s->hdrs & ICE_FLOW_SEG_HDR_IPV4)
 | |
| 			return ICE_RSS_INNER_HEADERS_W_OUTER_IPV4;
 | |
| 		if (s->hdrs & ICE_FLOW_SEG_HDR_IPV6)
 | |
| 			return ICE_RSS_INNER_HEADERS_W_OUTER_IPV6;
 | |
| 	}
 | |
| 
 | |
| 	return ICE_RSS_ANY_HEADERS;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| ice_rss_match_prof(struct ice_rss_cfg *r, struct ice_flow_prof *prof,
 | |
| 		   enum ice_rss_cfg_hdr_type hdr_type)
 | |
| {
 | |
| 	return (r->hash.hdr_type == hdr_type &&
 | |
| 		r->hash.hash_flds == prof->segs[prof->segs_cnt - 1].match &&
 | |
| 		r->hash.addl_hdrs == prof->segs[prof->segs_cnt - 1].hdrs);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_rem_rss_list - remove RSS configuration from list
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @vsi_handle: software VSI handle
 | |
|  * @prof: pointer to flow profile
 | |
|  *
 | |
|  * Assumption: lock has already been acquired for RSS list
 | |
|  */
 | |
| static void
 | |
| ice_rem_rss_list(struct ice_hw *hw, u16 vsi_handle, struct ice_flow_prof *prof)
 | |
| {
 | |
| 	enum ice_rss_cfg_hdr_type hdr_type;
 | |
| 	struct ice_rss_cfg *r, *tmp;
 | |
| 
 | |
| 	/* Search for RSS hash fields associated to the VSI that match the
 | |
| 	 * hash configurations associated to the flow profile. If found
 | |
| 	 * remove from the RSS entry list of the VSI context and delete entry.
 | |
| 	 */
 | |
| 	hdr_type = ice_get_rss_hdr_type(prof);
 | |
| 	list_for_each_entry_safe(r, tmp, &hw->rss_list_head, l_entry)
 | |
| 		if (ice_rss_match_prof(r, prof, hdr_type)) {
 | |
| 			clear_bit(vsi_handle, r->vsis);
 | |
| 			if (bitmap_empty(r->vsis, ICE_MAX_VSI)) {
 | |
| 				list_del(&r->l_entry);
 | |
| 				devm_kfree(ice_hw_to_dev(hw), r);
 | |
| 			}
 | |
| 			return;
 | |
| 		}
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_add_rss_list - add RSS configuration to list
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @vsi_handle: software VSI handle
 | |
|  * @prof: pointer to flow profile
 | |
|  *
 | |
|  * Assumption: lock has already been acquired for RSS list
 | |
|  */
 | |
| static int
 | |
| ice_add_rss_list(struct ice_hw *hw, u16 vsi_handle, struct ice_flow_prof *prof)
 | |
| {
 | |
| 	enum ice_rss_cfg_hdr_type hdr_type;
 | |
| 	struct ice_rss_cfg *r, *rss_cfg;
 | |
| 
 | |
| 	hdr_type = ice_get_rss_hdr_type(prof);
 | |
| 	list_for_each_entry(r, &hw->rss_list_head, l_entry)
 | |
| 		if (ice_rss_match_prof(r, prof, hdr_type)) {
 | |
| 			set_bit(vsi_handle, r->vsis);
 | |
| 			return 0;
 | |
| 		}
 | |
| 
 | |
| 	rss_cfg = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*rss_cfg),
 | |
| 			       GFP_KERNEL);
 | |
| 	if (!rss_cfg)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	rss_cfg->hash.hash_flds = prof->segs[prof->segs_cnt - 1].match;
 | |
| 	rss_cfg->hash.addl_hdrs = prof->segs[prof->segs_cnt - 1].hdrs;
 | |
| 	rss_cfg->hash.hdr_type = hdr_type;
 | |
| 	rss_cfg->hash.symm = prof->symm;
 | |
| 	set_bit(vsi_handle, rss_cfg->vsis);
 | |
| 
 | |
| 	list_add_tail(&rss_cfg->l_entry, &hw->rss_list_head);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_rss_config_xor_word - set the HSYMM registers for one input set word
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @prof_id: RSS hardware profile id
 | |
|  * @src: the FV index used by the protocol's source field
 | |
|  * @dst: the FV index used by the protocol's destination field
 | |
|  *
 | |
|  * Write to the HSYMM register with the index of @src FV the value of the @dst
 | |
|  * FV index. This will tell the hardware to XOR HSYMM[src] with INSET[dst]
 | |
|  * while calculating the RSS input set.
 | |
|  */
 | |
| static void
 | |
| ice_rss_config_xor_word(struct ice_hw *hw, u8 prof_id, u8 src, u8 dst)
 | |
| {
 | |
| 	u32 val, reg, bits_shift;
 | |
| 	u8 reg_idx;
 | |
| 
 | |
| 	reg_idx = src / GLQF_HSYMM_REG_SIZE;
 | |
| 	bits_shift = ((src % GLQF_HSYMM_REG_SIZE) << 3);
 | |
| 	val = dst | GLQF_HSYMM_ENABLE_BIT;
 | |
| 
 | |
| 	reg = rd32(hw, GLQF_HSYMM(prof_id, reg_idx));
 | |
| 	reg = (reg & ~(0xff << bits_shift)) | (val << bits_shift);
 | |
| 	wr32(hw, GLQF_HSYMM(prof_id, reg_idx), reg);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_rss_config_xor - set the symmetric registers for a profile's protocol
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @prof_id: RSS hardware profile id
 | |
|  * @src: the FV index used by the protocol's source field
 | |
|  * @dst: the FV index used by the protocol's destination field
 | |
|  * @len: length of the source/destination fields in words
 | |
|  */
 | |
| static void
 | |
| ice_rss_config_xor(struct ice_hw *hw, u8 prof_id, u8 src, u8 dst, u8 len)
 | |
| {
 | |
| 	int fv_last_word =
 | |
| 		ICE_FLOW_SW_FIELD_VECTOR_MAX / ICE_FLOW_FV_EXTRACT_SZ - 1;
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; i < len; i++) {
 | |
| 		ice_rss_config_xor_word(hw, prof_id,
 | |
| 					/* Yes, field vector in GLQF_HSYMM and
 | |
| 					 * GLQF_HINSET is inversed!
 | |
| 					 */
 | |
| 					fv_last_word - (src + i),
 | |
| 					fv_last_word - (dst + i));
 | |
| 		ice_rss_config_xor_word(hw, prof_id,
 | |
| 					fv_last_word - (dst + i),
 | |
| 					fv_last_word - (src + i));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_rss_set_symm - set the symmetric settings for an RSS profile
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @prof: pointer to flow profile
 | |
|  *
 | |
|  * The symmetric hash will result from XORing the protocol's fields with
 | |
|  * indexes in GLQF_HSYMM and GLQF_HINSET. This function configures the profile's
 | |
|  * GLQF_HSYMM registers.
 | |
|  */
 | |
| static void ice_rss_set_symm(struct ice_hw *hw, struct ice_flow_prof *prof)
 | |
| {
 | |
| 	struct ice_prof_map *map;
 | |
| 	u8 prof_id, m;
 | |
| 
 | |
| 	mutex_lock(&hw->blk[ICE_BLK_RSS].es.prof_map_lock);
 | |
| 	map = ice_search_prof_id(hw, ICE_BLK_RSS, prof->id);
 | |
| 	if (map)
 | |
| 		prof_id = map->prof_id;
 | |
| 	mutex_unlock(&hw->blk[ICE_BLK_RSS].es.prof_map_lock);
 | |
| 
 | |
| 	if (!map)
 | |
| 		return;
 | |
| 
 | |
| 	/* clear to default */
 | |
| 	for (m = 0; m < GLQF_HSYMM_REG_PER_PROF; m++)
 | |
| 		wr32(hw, GLQF_HSYMM(prof_id, m), 0);
 | |
| 
 | |
| 	if (prof->symm) {
 | |
| 		struct ice_flow_seg_xtrct *ipv4_src, *ipv4_dst;
 | |
| 		struct ice_flow_seg_xtrct *ipv6_src, *ipv6_dst;
 | |
| 		struct ice_flow_seg_xtrct *sctp_src, *sctp_dst;
 | |
| 		struct ice_flow_seg_xtrct *tcp_src, *tcp_dst;
 | |
| 		struct ice_flow_seg_xtrct *udp_src, *udp_dst;
 | |
| 		struct ice_flow_seg_info *seg;
 | |
| 
 | |
| 		seg = &prof->segs[prof->segs_cnt - 1];
 | |
| 
 | |
| 		ipv4_src = &seg->fields[ICE_FLOW_FIELD_IDX_IPV4_SA].xtrct;
 | |
| 		ipv4_dst = &seg->fields[ICE_FLOW_FIELD_IDX_IPV4_DA].xtrct;
 | |
| 
 | |
| 		ipv6_src = &seg->fields[ICE_FLOW_FIELD_IDX_IPV6_SA].xtrct;
 | |
| 		ipv6_dst = &seg->fields[ICE_FLOW_FIELD_IDX_IPV6_DA].xtrct;
 | |
| 
 | |
| 		tcp_src = &seg->fields[ICE_FLOW_FIELD_IDX_TCP_SRC_PORT].xtrct;
 | |
| 		tcp_dst = &seg->fields[ICE_FLOW_FIELD_IDX_TCP_DST_PORT].xtrct;
 | |
| 
 | |
| 		udp_src = &seg->fields[ICE_FLOW_FIELD_IDX_UDP_SRC_PORT].xtrct;
 | |
| 		udp_dst = &seg->fields[ICE_FLOW_FIELD_IDX_UDP_DST_PORT].xtrct;
 | |
| 
 | |
| 		sctp_src = &seg->fields[ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT].xtrct;
 | |
| 		sctp_dst = &seg->fields[ICE_FLOW_FIELD_IDX_SCTP_DST_PORT].xtrct;
 | |
| 
 | |
| 		/* xor IPv4 */
 | |
| 		if (ipv4_src->prot_id != 0 && ipv4_dst->prot_id != 0)
 | |
| 			ice_rss_config_xor(hw, prof_id,
 | |
| 					   ipv4_src->idx, ipv4_dst->idx, 2);
 | |
| 
 | |
| 		/* xor IPv6 */
 | |
| 		if (ipv6_src->prot_id != 0 && ipv6_dst->prot_id != 0)
 | |
| 			ice_rss_config_xor(hw, prof_id,
 | |
| 					   ipv6_src->idx, ipv6_dst->idx, 8);
 | |
| 
 | |
| 		/* xor TCP */
 | |
| 		if (tcp_src->prot_id != 0 && tcp_dst->prot_id != 0)
 | |
| 			ice_rss_config_xor(hw, prof_id,
 | |
| 					   tcp_src->idx, tcp_dst->idx, 1);
 | |
| 
 | |
| 		/* xor UDP */
 | |
| 		if (udp_src->prot_id != 0 && udp_dst->prot_id != 0)
 | |
| 			ice_rss_config_xor(hw, prof_id,
 | |
| 					   udp_src->idx, udp_dst->idx, 1);
 | |
| 
 | |
| 		/* xor SCTP */
 | |
| 		if (sctp_src->prot_id != 0 && sctp_dst->prot_id != 0)
 | |
| 			ice_rss_config_xor(hw, prof_id,
 | |
| 					   sctp_src->idx, sctp_dst->idx, 1);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_add_rss_cfg_sync - add an RSS configuration
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @vsi_handle: software VSI handle
 | |
|  * @cfg: configure parameters
 | |
|  *
 | |
|  * Assumption: lock has already been acquired for RSS list
 | |
|  */
 | |
| static int
 | |
| ice_add_rss_cfg_sync(struct ice_hw *hw, u16 vsi_handle,
 | |
| 		     const struct ice_rss_hash_cfg *cfg)
 | |
| {
 | |
| 	const enum ice_block blk = ICE_BLK_RSS;
 | |
| 	struct ice_flow_prof *prof = NULL;
 | |
| 	struct ice_flow_seg_info *segs;
 | |
| 	u8 segs_cnt;
 | |
| 	int status;
 | |
| 
 | |
| 	segs_cnt = (cfg->hdr_type == ICE_RSS_OUTER_HEADERS) ?
 | |
| 			ICE_FLOW_SEG_SINGLE : ICE_FLOW_SEG_MAX;
 | |
| 
 | |
| 	segs = kcalloc(segs_cnt, sizeof(*segs), GFP_KERNEL);
 | |
| 	if (!segs)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	/* Construct the packet segment info from the hashed fields */
 | |
| 	status = ice_flow_set_rss_seg_info(segs, segs_cnt, cfg);
 | |
| 	if (status)
 | |
| 		goto exit;
 | |
| 
 | |
| 	/* Search for a flow profile that has matching headers, hash fields,
 | |
| 	 * symm and has the input VSI associated to it. If found, no further
 | |
| 	 * operations required and exit.
 | |
| 	 */
 | |
| 	prof = ice_flow_find_prof_conds(hw, blk, ICE_FLOW_RX, segs, segs_cnt,
 | |
| 					cfg->symm, vsi_handle,
 | |
| 					ICE_FLOW_FIND_PROF_CHK_FLDS |
 | |
| 					ICE_FLOW_FIND_PROF_CHK_SYMM |
 | |
| 					ICE_FLOW_FIND_PROF_CHK_VSI);
 | |
| 	if (prof)
 | |
| 		goto exit;
 | |
| 
 | |
| 	/* Check if a flow profile exists with the same protocol headers and
 | |
| 	 * associated with the input VSI. If so disassociate the VSI from
 | |
| 	 * this profile. The VSI will be added to a new profile created with
 | |
| 	 * the protocol header and new hash field configuration.
 | |
| 	 */
 | |
| 	prof = ice_flow_find_prof_conds(hw, blk, ICE_FLOW_RX, segs, segs_cnt,
 | |
| 					cfg->symm, vsi_handle,
 | |
| 					ICE_FLOW_FIND_PROF_CHK_VSI);
 | |
| 	if (prof) {
 | |
| 		status = ice_flow_disassoc_prof(hw, blk, prof, vsi_handle);
 | |
| 		if (!status)
 | |
| 			ice_rem_rss_list(hw, vsi_handle, prof);
 | |
| 		else
 | |
| 			goto exit;
 | |
| 
 | |
| 		/* Remove profile if it has no VSIs associated */
 | |
| 		if (bitmap_empty(prof->vsis, ICE_MAX_VSI)) {
 | |
| 			status = ice_flow_rem_prof(hw, blk, prof->id);
 | |
| 			if (status)
 | |
| 				goto exit;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Search for a profile that has the same match fields and symmetric
 | |
| 	 * setting. If this exists then associate the VSI to this profile.
 | |
| 	 */
 | |
| 	prof = ice_flow_find_prof_conds(hw, blk, ICE_FLOW_RX, segs, segs_cnt,
 | |
| 					cfg->symm, vsi_handle,
 | |
| 					ICE_FLOW_FIND_PROF_CHK_SYMM |
 | |
| 					ICE_FLOW_FIND_PROF_CHK_FLDS);
 | |
| 	if (prof) {
 | |
| 		status = ice_flow_assoc_prof(hw, blk, prof, vsi_handle);
 | |
| 		if (!status)
 | |
| 			status = ice_add_rss_list(hw, vsi_handle, prof);
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	/* Create a new flow profile with packet segment information. */
 | |
| 	status = ice_flow_add_prof(hw, blk, ICE_FLOW_RX,
 | |
| 				   segs, segs_cnt, cfg->symm, &prof);
 | |
| 	if (status)
 | |
| 		goto exit;
 | |
| 
 | |
| 	prof->symm = cfg->symm;
 | |
| 	ice_rss_set_symm(hw, prof);
 | |
| 	status = ice_flow_assoc_prof(hw, blk, prof, vsi_handle);
 | |
| 	/* If association to a new flow profile failed then this profile can
 | |
| 	 * be removed.
 | |
| 	 */
 | |
| 	if (status) {
 | |
| 		ice_flow_rem_prof(hw, blk, prof->id);
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	status = ice_add_rss_list(hw, vsi_handle, prof);
 | |
| 
 | |
| exit:
 | |
| 	kfree(segs);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_add_rss_cfg - add an RSS configuration with specified hashed fields
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @vsi: VSI to add the RSS configuration to
 | |
|  * @cfg: configure parameters
 | |
|  *
 | |
|  * This function will generate a flow profile based on fields associated with
 | |
|  * the input fields to hash on, the flow type and use the VSI number to add
 | |
|  * a flow entry to the profile.
 | |
|  */
 | |
| int
 | |
| ice_add_rss_cfg(struct ice_hw *hw, struct ice_vsi *vsi,
 | |
| 		const struct ice_rss_hash_cfg *cfg)
 | |
| {
 | |
| 	struct ice_rss_hash_cfg local_cfg;
 | |
| 	u16 vsi_handle;
 | |
| 	int status;
 | |
| 
 | |
| 	if (!vsi)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	vsi_handle = vsi->idx;
 | |
| 	if (!ice_is_vsi_valid(hw, vsi_handle) ||
 | |
| 	    !cfg || cfg->hdr_type > ICE_RSS_ANY_HEADERS ||
 | |
| 	    cfg->hash_flds == ICE_HASH_INVALID)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	mutex_lock(&hw->rss_locks);
 | |
| 	local_cfg = *cfg;
 | |
| 	if (cfg->hdr_type < ICE_RSS_ANY_HEADERS) {
 | |
| 		status = ice_add_rss_cfg_sync(hw, vsi_handle, &local_cfg);
 | |
| 	} else {
 | |
| 		local_cfg.hdr_type = ICE_RSS_OUTER_HEADERS;
 | |
| 		status = ice_add_rss_cfg_sync(hw, vsi_handle, &local_cfg);
 | |
| 		if (!status) {
 | |
| 			local_cfg.hdr_type = ICE_RSS_INNER_HEADERS;
 | |
| 			status = ice_add_rss_cfg_sync(hw, vsi_handle,
 | |
| 						      &local_cfg);
 | |
| 		}
 | |
| 	}
 | |
| 	mutex_unlock(&hw->rss_locks);
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_rem_rss_cfg_sync - remove an existing RSS configuration
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @vsi_handle: software VSI handle
 | |
|  * @cfg: configure parameters
 | |
|  *
 | |
|  * Assumption: lock has already been acquired for RSS list
 | |
|  */
 | |
| static int
 | |
| ice_rem_rss_cfg_sync(struct ice_hw *hw, u16 vsi_handle,
 | |
| 		     const struct ice_rss_hash_cfg *cfg)
 | |
| {
 | |
| 	const enum ice_block blk = ICE_BLK_RSS;
 | |
| 	struct ice_flow_seg_info *segs;
 | |
| 	struct ice_flow_prof *prof;
 | |
| 	u8 segs_cnt;
 | |
| 	int status;
 | |
| 
 | |
| 	segs_cnt = (cfg->hdr_type == ICE_RSS_OUTER_HEADERS) ?
 | |
| 			ICE_FLOW_SEG_SINGLE : ICE_FLOW_SEG_MAX;
 | |
| 	segs = kcalloc(segs_cnt, sizeof(*segs), GFP_KERNEL);
 | |
| 	if (!segs)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	/* Construct the packet segment info from the hashed fields */
 | |
| 	status = ice_flow_set_rss_seg_info(segs, segs_cnt, cfg);
 | |
| 	if (status)
 | |
| 		goto out;
 | |
| 
 | |
| 	prof = ice_flow_find_prof_conds(hw, blk, ICE_FLOW_RX, segs, segs_cnt,
 | |
| 					cfg->symm, vsi_handle,
 | |
| 					ICE_FLOW_FIND_PROF_CHK_FLDS);
 | |
| 	if (!prof) {
 | |
| 		status = -ENOENT;
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	status = ice_flow_disassoc_prof(hw, blk, prof, vsi_handle);
 | |
| 	if (status)
 | |
| 		goto out;
 | |
| 
 | |
| 	/* Remove RSS configuration from VSI context before deleting
 | |
| 	 * the flow profile.
 | |
| 	 */
 | |
| 	ice_rem_rss_list(hw, vsi_handle, prof);
 | |
| 
 | |
| 	if (bitmap_empty(prof->vsis, ICE_MAX_VSI))
 | |
| 		status = ice_flow_rem_prof(hw, blk, prof->id);
 | |
| 
 | |
| out:
 | |
| 	kfree(segs);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_rem_rss_cfg - remove an existing RSS config with matching hashed fields
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @vsi_handle: software VSI handle
 | |
|  * @cfg: configure parameters
 | |
|  *
 | |
|  * This function will lookup the flow profile based on the input
 | |
|  * hash field bitmap, iterate through the profile entry list of
 | |
|  * that profile and find entry associated with input VSI to be
 | |
|  * removed. Calls are made to underlying flow apis which will in
 | |
|  * turn build or update buffers for RSS XLT1 section.
 | |
|  */
 | |
| int
 | |
| ice_rem_rss_cfg(struct ice_hw *hw, u16 vsi_handle,
 | |
| 		const struct ice_rss_hash_cfg *cfg)
 | |
| {
 | |
| 	struct ice_rss_hash_cfg local_cfg;
 | |
| 	int status;
 | |
| 
 | |
| 	if (!ice_is_vsi_valid(hw, vsi_handle) ||
 | |
| 	    !cfg || cfg->hdr_type > ICE_RSS_ANY_HEADERS ||
 | |
| 	    cfg->hash_flds == ICE_HASH_INVALID)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	mutex_lock(&hw->rss_locks);
 | |
| 	local_cfg = *cfg;
 | |
| 	if (cfg->hdr_type < ICE_RSS_ANY_HEADERS) {
 | |
| 		status = ice_rem_rss_cfg_sync(hw, vsi_handle, &local_cfg);
 | |
| 	} else {
 | |
| 		local_cfg.hdr_type = ICE_RSS_OUTER_HEADERS;
 | |
| 		status = ice_rem_rss_cfg_sync(hw, vsi_handle, &local_cfg);
 | |
| 		if (!status) {
 | |
| 			local_cfg.hdr_type = ICE_RSS_INNER_HEADERS;
 | |
| 			status = ice_rem_rss_cfg_sync(hw, vsi_handle,
 | |
| 						      &local_cfg);
 | |
| 		}
 | |
| 	}
 | |
| 	mutex_unlock(&hw->rss_locks);
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /* Mapping of AVF hash bit fields to an L3-L4 hash combination.
 | |
|  * As the ice_flow_avf_hdr_field represent individual bit shifts in a hash,
 | |
|  * convert its values to their appropriate flow L3, L4 values.
 | |
|  */
 | |
| #define ICE_FLOW_AVF_RSS_IPV4_MASKS \
 | |
| 	(BIT_ULL(ICE_AVF_FLOW_FIELD_IPV4_OTHER) | \
 | |
| 	 BIT_ULL(ICE_AVF_FLOW_FIELD_FRAG_IPV4))
 | |
| #define ICE_FLOW_AVF_RSS_TCP_IPV4_MASKS \
 | |
| 	(BIT_ULL(ICE_AVF_FLOW_FIELD_IPV4_TCP_SYN_NO_ACK) | \
 | |
| 	 BIT_ULL(ICE_AVF_FLOW_FIELD_IPV4_TCP))
 | |
| #define ICE_FLOW_AVF_RSS_UDP_IPV4_MASKS \
 | |
| 	(BIT_ULL(ICE_AVF_FLOW_FIELD_UNICAST_IPV4_UDP) | \
 | |
| 	 BIT_ULL(ICE_AVF_FLOW_FIELD_MULTICAST_IPV4_UDP) | \
 | |
| 	 BIT_ULL(ICE_AVF_FLOW_FIELD_IPV4_UDP))
 | |
| #define ICE_FLOW_AVF_RSS_ALL_IPV4_MASKS \
 | |
| 	(ICE_FLOW_AVF_RSS_TCP_IPV4_MASKS | ICE_FLOW_AVF_RSS_UDP_IPV4_MASKS | \
 | |
| 	 ICE_FLOW_AVF_RSS_IPV4_MASKS | BIT_ULL(ICE_AVF_FLOW_FIELD_IPV4_SCTP))
 | |
| 
 | |
| #define ICE_FLOW_AVF_RSS_IPV6_MASKS \
 | |
| 	(BIT_ULL(ICE_AVF_FLOW_FIELD_IPV6_OTHER) | \
 | |
| 	 BIT_ULL(ICE_AVF_FLOW_FIELD_FRAG_IPV6))
 | |
| #define ICE_FLOW_AVF_RSS_UDP_IPV6_MASKS \
 | |
| 	(BIT_ULL(ICE_AVF_FLOW_FIELD_UNICAST_IPV6_UDP) | \
 | |
| 	 BIT_ULL(ICE_AVF_FLOW_FIELD_MULTICAST_IPV6_UDP) | \
 | |
| 	 BIT_ULL(ICE_AVF_FLOW_FIELD_IPV6_UDP))
 | |
| #define ICE_FLOW_AVF_RSS_TCP_IPV6_MASKS \
 | |
| 	(BIT_ULL(ICE_AVF_FLOW_FIELD_IPV6_TCP_SYN_NO_ACK) | \
 | |
| 	 BIT_ULL(ICE_AVF_FLOW_FIELD_IPV6_TCP))
 | |
| #define ICE_FLOW_AVF_RSS_ALL_IPV6_MASKS \
 | |
| 	(ICE_FLOW_AVF_RSS_TCP_IPV6_MASKS | ICE_FLOW_AVF_RSS_UDP_IPV6_MASKS | \
 | |
| 	 ICE_FLOW_AVF_RSS_IPV6_MASKS | BIT_ULL(ICE_AVF_FLOW_FIELD_IPV6_SCTP))
 | |
| 
 | |
| /**
 | |
|  * ice_add_avf_rss_cfg - add an RSS configuration for AVF driver
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @vsi: VF's VSI
 | |
|  * @avf_hash: hash bit fields (ICE_AVF_FLOW_FIELD_*) to configure
 | |
|  *
 | |
|  * This function will take the hash bitmap provided by the AVF driver via a
 | |
|  * message, convert it to ICE-compatible values, and configure RSS flow
 | |
|  * profiles.
 | |
|  */
 | |
| int ice_add_avf_rss_cfg(struct ice_hw *hw, struct ice_vsi *vsi, u64 avf_hash)
 | |
| {
 | |
| 	struct ice_rss_hash_cfg hcfg;
 | |
| 	u16 vsi_handle;
 | |
| 	int status = 0;
 | |
| 	u64 hash_flds;
 | |
| 
 | |
| 	if (!vsi)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	vsi_handle = vsi->idx;
 | |
| 	if (avf_hash == ICE_AVF_FLOW_FIELD_INVALID ||
 | |
| 	    !ice_is_vsi_valid(hw, vsi_handle))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	/* Make sure no unsupported bits are specified */
 | |
| 	if (avf_hash & ~(ICE_FLOW_AVF_RSS_ALL_IPV4_MASKS |
 | |
| 			 ICE_FLOW_AVF_RSS_ALL_IPV6_MASKS))
 | |
| 		return -EIO;
 | |
| 
 | |
| 	hash_flds = avf_hash;
 | |
| 
 | |
| 	/* Always create an L3 RSS configuration for any L4 RSS configuration */
 | |
| 	if (hash_flds & ICE_FLOW_AVF_RSS_ALL_IPV4_MASKS)
 | |
| 		hash_flds |= ICE_FLOW_AVF_RSS_IPV4_MASKS;
 | |
| 
 | |
| 	if (hash_flds & ICE_FLOW_AVF_RSS_ALL_IPV6_MASKS)
 | |
| 		hash_flds |= ICE_FLOW_AVF_RSS_IPV6_MASKS;
 | |
| 
 | |
| 	/* Create the corresponding RSS configuration for each valid hash bit */
 | |
| 	while (hash_flds) {
 | |
| 		u64 rss_hash = ICE_HASH_INVALID;
 | |
| 
 | |
| 		if (hash_flds & ICE_FLOW_AVF_RSS_ALL_IPV4_MASKS) {
 | |
| 			if (hash_flds & ICE_FLOW_AVF_RSS_IPV4_MASKS) {
 | |
| 				rss_hash = ICE_FLOW_HASH_IPV4;
 | |
| 				hash_flds &= ~ICE_FLOW_AVF_RSS_IPV4_MASKS;
 | |
| 			} else if (hash_flds &
 | |
| 				   ICE_FLOW_AVF_RSS_TCP_IPV4_MASKS) {
 | |
| 				rss_hash = ICE_FLOW_HASH_IPV4 |
 | |
| 					ICE_FLOW_HASH_TCP_PORT;
 | |
| 				hash_flds &= ~ICE_FLOW_AVF_RSS_TCP_IPV4_MASKS;
 | |
| 			} else if (hash_flds &
 | |
| 				   ICE_FLOW_AVF_RSS_UDP_IPV4_MASKS) {
 | |
| 				rss_hash = ICE_FLOW_HASH_IPV4 |
 | |
| 					ICE_FLOW_HASH_UDP_PORT;
 | |
| 				hash_flds &= ~ICE_FLOW_AVF_RSS_UDP_IPV4_MASKS;
 | |
| 			} else if (hash_flds &
 | |
| 				   BIT_ULL(ICE_AVF_FLOW_FIELD_IPV4_SCTP)) {
 | |
| 				rss_hash = ICE_FLOW_HASH_IPV4 |
 | |
| 					ICE_FLOW_HASH_SCTP_PORT;
 | |
| 				hash_flds &=
 | |
| 					~BIT_ULL(ICE_AVF_FLOW_FIELD_IPV4_SCTP);
 | |
| 			}
 | |
| 		} else if (hash_flds & ICE_FLOW_AVF_RSS_ALL_IPV6_MASKS) {
 | |
| 			if (hash_flds & ICE_FLOW_AVF_RSS_IPV6_MASKS) {
 | |
| 				rss_hash = ICE_FLOW_HASH_IPV6;
 | |
| 				hash_flds &= ~ICE_FLOW_AVF_RSS_IPV6_MASKS;
 | |
| 			} else if (hash_flds &
 | |
| 				   ICE_FLOW_AVF_RSS_TCP_IPV6_MASKS) {
 | |
| 				rss_hash = ICE_FLOW_HASH_IPV6 |
 | |
| 					ICE_FLOW_HASH_TCP_PORT;
 | |
| 				hash_flds &= ~ICE_FLOW_AVF_RSS_TCP_IPV6_MASKS;
 | |
| 			} else if (hash_flds &
 | |
| 				   ICE_FLOW_AVF_RSS_UDP_IPV6_MASKS) {
 | |
| 				rss_hash = ICE_FLOW_HASH_IPV6 |
 | |
| 					ICE_FLOW_HASH_UDP_PORT;
 | |
| 				hash_flds &= ~ICE_FLOW_AVF_RSS_UDP_IPV6_MASKS;
 | |
| 			} else if (hash_flds &
 | |
| 				   BIT_ULL(ICE_AVF_FLOW_FIELD_IPV6_SCTP)) {
 | |
| 				rss_hash = ICE_FLOW_HASH_IPV6 |
 | |
| 					ICE_FLOW_HASH_SCTP_PORT;
 | |
| 				hash_flds &=
 | |
| 					~BIT_ULL(ICE_AVF_FLOW_FIELD_IPV6_SCTP);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (rss_hash == ICE_HASH_INVALID)
 | |
| 			return -EIO;
 | |
| 
 | |
| 		hcfg.addl_hdrs = ICE_FLOW_SEG_HDR_NONE;
 | |
| 		hcfg.hash_flds = rss_hash;
 | |
| 		hcfg.hdr_type = ICE_RSS_ANY_HEADERS;
 | |
| 		hcfg.symm = false;
 | |
| 		status = ice_add_rss_cfg(hw, vsi, &hcfg);
 | |
| 		if (status)
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| static bool rss_cfg_symm_valid(u64 hfld)
 | |
| {
 | |
| 	return !((!!(hfld & ICE_FLOW_HASH_FLD_IPV4_SA) ^
 | |
| 		  !!(hfld & ICE_FLOW_HASH_FLD_IPV4_DA)) ||
 | |
| 		 (!!(hfld & ICE_FLOW_HASH_FLD_IPV6_SA) ^
 | |
| 		  !!(hfld & ICE_FLOW_HASH_FLD_IPV6_DA)) ||
 | |
| 		 (!!(hfld & ICE_FLOW_HASH_FLD_TCP_SRC_PORT) ^
 | |
| 		  !!(hfld & ICE_FLOW_HASH_FLD_TCP_DST_PORT)) ||
 | |
| 		 (!!(hfld & ICE_FLOW_HASH_FLD_UDP_SRC_PORT) ^
 | |
| 		  !!(hfld & ICE_FLOW_HASH_FLD_UDP_DST_PORT)) ||
 | |
| 		 (!!(hfld & ICE_FLOW_HASH_FLD_SCTP_SRC_PORT) ^
 | |
| 		  !!(hfld & ICE_FLOW_HASH_FLD_SCTP_DST_PORT)));
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_set_rss_cfg_symm - set symmtery for all VSI's RSS configurations
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @vsi: VSI to set/unset Symmetric RSS
 | |
|  * @symm: TRUE to set Symmetric RSS hashing
 | |
|  */
 | |
| int ice_set_rss_cfg_symm(struct ice_hw *hw, struct ice_vsi *vsi, bool symm)
 | |
| {
 | |
| 	struct ice_rss_hash_cfg	local;
 | |
| 	struct ice_rss_cfg *r, *tmp;
 | |
| 	u16 vsi_handle = vsi->idx;
 | |
| 	int status = 0;
 | |
| 
 | |
| 	if (!ice_is_vsi_valid(hw, vsi_handle))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	mutex_lock(&hw->rss_locks);
 | |
| 	list_for_each_entry_safe(r, tmp, &hw->rss_list_head, l_entry) {
 | |
| 		if (test_bit(vsi_handle, r->vsis) && r->hash.symm != symm) {
 | |
| 			local = r->hash;
 | |
| 			local.symm = symm;
 | |
| 			if (symm && !rss_cfg_symm_valid(r->hash.hash_flds))
 | |
| 				continue;
 | |
| 
 | |
| 			status = ice_add_rss_cfg_sync(hw, vsi_handle, &local);
 | |
| 			if (status)
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 	mutex_unlock(&hw->rss_locks);
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_replay_rss_cfg - replay RSS configurations associated with VSI
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @vsi_handle: software VSI handle
 | |
|  */
 | |
| int ice_replay_rss_cfg(struct ice_hw *hw, u16 vsi_handle)
 | |
| {
 | |
| 	struct ice_rss_cfg *r;
 | |
| 	int status = 0;
 | |
| 
 | |
| 	if (!ice_is_vsi_valid(hw, vsi_handle))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	mutex_lock(&hw->rss_locks);
 | |
| 	list_for_each_entry(r, &hw->rss_list_head, l_entry) {
 | |
| 		if (test_bit(vsi_handle, r->vsis)) {
 | |
| 			status = ice_add_rss_cfg_sync(hw, vsi_handle, &r->hash);
 | |
| 			if (status)
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 	mutex_unlock(&hw->rss_locks);
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ice_get_rss_cfg - returns hashed fields for the given header types
 | |
|  * @hw: pointer to the hardware structure
 | |
|  * @vsi_handle: software VSI handle
 | |
|  * @hdrs: protocol header type
 | |
|  * @symm: whether the RSS is symmetric (bool, output)
 | |
|  *
 | |
|  * This function will return the match fields of the first instance of flow
 | |
|  * profile having the given header types and containing input VSI
 | |
|  */
 | |
| u64 ice_get_rss_cfg(struct ice_hw *hw, u16 vsi_handle, u32 hdrs, bool *symm)
 | |
| {
 | |
| 	u64 rss_hash = ICE_HASH_INVALID;
 | |
| 	struct ice_rss_cfg *r;
 | |
| 
 | |
| 	/* verify if the protocol header is non zero and VSI is valid */
 | |
| 	if (hdrs == ICE_FLOW_SEG_HDR_NONE || !ice_is_vsi_valid(hw, vsi_handle))
 | |
| 		return ICE_HASH_INVALID;
 | |
| 
 | |
| 	mutex_lock(&hw->rss_locks);
 | |
| 	list_for_each_entry(r, &hw->rss_list_head, l_entry)
 | |
| 		if (test_bit(vsi_handle, r->vsis) &&
 | |
| 		    r->hash.addl_hdrs == hdrs) {
 | |
| 			rss_hash = r->hash.hash_flds;
 | |
| 			*symm = r->hash.symm;
 | |
| 			break;
 | |
| 		}
 | |
| 	mutex_unlock(&hw->rss_locks);
 | |
| 
 | |
| 	return rss_hash;
 | |
| }
 |