diff options
| author | Jakub Kicinski <kuba@kernel.org> | 2022-11-04 19:54:29 -0700 | 
|---|---|---|
| committer | Jakub Kicinski <kuba@kernel.org> | 2022-11-04 19:54:29 -0700 | 
| commit | bcc8f44acac1f5c0cc80abe5658857903a6bb68b (patch) | |
| tree | 1ce633fe7b1d3133c47eb6f73275f316700e2fc3 | |
| parent | 8e0aa1ff44ca30b0b9df95dfa72003522d9a088d (diff) | |
| parent | 5d1d24da00dba75f94f1640717b3fd2a243e4b5f (diff) | |
Merge branch 'sfc-add-basic-flower-matches-to-offload'
Edward Cree says:
====================
sfc: add basic flower matches to offload
Support offloading TC flower rules with matches on L2-L4 fields.
====================
Link: https://lore.kernel.org/r/cover.1667412458.git.ecree.xilinx@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
| -rw-r--r-- | drivers/net/ethernet/sfc/mae.c | 131 | ||||
| -rw-r--r-- | drivers/net/ethernet/sfc/mcdi.h | 12 | ||||
| -rw-r--r-- | drivers/net/ethernet/sfc/tc.c | 137 | ||||
| -rw-r--r-- | drivers/net/ethernet/sfc/tc.h | 16 | 
4 files changed, 285 insertions, 11 deletions
| diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c index 6f472ea0638a..1e605e2a08c5 100644 --- a/drivers/net/ethernet/sfc/mae.c +++ b/drivers/net/ethernet/sfc/mae.c @@ -250,6 +250,32 @@ static int efx_mae_match_check_cap_typ(u8 support, enum mask_type typ)  	}  } +/* Validate field mask against hardware capabilities.  Captures caller's 'rc' */ +#define CHECK(_mcdi, _field)	({					       \ +	enum mask_type typ = classify_mask((const u8 *)&mask->_field,	       \ +					   sizeof(mask->_field));	       \ +									       \ +	rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ ## _mcdi],\ +					 typ);				       \ +	if (rc)								       \ +		NL_SET_ERR_MSG_FMT_MOD(extack,				       \ +				       "No support for %s mask in field %s",   \ +				       mask_type_name(typ), #_field);	       \ +	rc;								       \ +}) +/* Booleans need special handling */ +#define CHECK_BIT(_mcdi, _field)	({				       \ +	enum mask_type typ = mask->_field ? MASK_ONES : MASK_ZEROES;	       \ +									       \ +	rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ ## _mcdi],\ +					 typ);				       \ +	if (rc)								       \ +		NL_SET_ERR_MSG_FMT_MOD(extack,				       \ +				       "No support for %s mask in field %s",   \ +				       mask_type_name(typ), #_field);	       \ +	rc;								       \ +}) +  int efx_mae_match_check_caps(struct efx_nic *efx,  			     const struct efx_tc_match_fields *mask,  			     struct netlink_ext_ack *extack) @@ -269,8 +295,33 @@ int efx_mae_match_check_caps(struct efx_nic *efx,  				       mask_type_name(ingress_port_mask_type));  		return rc;  	} +	if (CHECK(ETHER_TYPE, eth_proto) || +	    CHECK(VLAN0_TCI, vlan_tci[0]) || +	    CHECK(VLAN0_PROTO, vlan_proto[0]) || +	    CHECK(VLAN1_TCI, vlan_tci[1]) || +	    CHECK(VLAN1_PROTO, vlan_proto[1]) || +	    CHECK(ETH_SADDR, eth_saddr) || +	    CHECK(ETH_DADDR, eth_daddr) || +	    CHECK(IP_PROTO, ip_proto) || +	    CHECK(IP_TOS, ip_tos) || +	    CHECK(IP_TTL, ip_ttl) || +	    CHECK(SRC_IP4, src_ip) || +	    CHECK(DST_IP4, dst_ip) || +#ifdef CONFIG_IPV6 +	    CHECK(SRC_IP6, src_ip6) || +	    CHECK(DST_IP6, dst_ip6) || +#endif +	    CHECK(L4_SPORT, l4_sport) || +	    CHECK(L4_DPORT, l4_dport) || +	    CHECK(TCP_FLAGS, tcp_flags) || +	    CHECK_BIT(IS_IP_FRAG, ip_frag) || +	    CHECK_BIT(IP_FIRST_FRAG, ip_firstfrag) || +	    CHECK(RECIRC_ID, recirc_id)) +		return rc;  	return 0;  } +#undef CHECK_BIT +#undef CHECK  static bool efx_mae_asl_id(u32 id)  { @@ -439,10 +490,90 @@ static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit),  	}  	MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR_MASK,  			      match->mask.ingress_port); +	EFX_POPULATE_DWORD_2(*_MCDI_STRUCT_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS), +			     MAE_FIELD_MASK_VALUE_PAIRS_V2_IS_IP_FRAG, +			     match->value.ip_frag, +			     MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_FIRST_FRAG, +			     match->value.ip_firstfrag); +	EFX_POPULATE_DWORD_2(*_MCDI_STRUCT_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS_MASK), +			     MAE_FIELD_MASK_VALUE_PAIRS_V2_IS_IP_FRAG, +			     match->mask.ip_frag, +			     MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_FIRST_FRAG, +			     match->mask.ip_firstfrag);  	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID,  			     match->value.recirc_id);  	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID_MASK,  			     match->mask.recirc_id); +	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETHER_TYPE_BE, +				match->value.eth_proto); +	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETHER_TYPE_BE_MASK, +				match->mask.eth_proto); +	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_TCI_BE, +				match->value.vlan_tci[0]); +	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_TCI_BE_MASK, +				match->mask.vlan_tci[0]); +	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_PROTO_BE, +				match->value.vlan_proto[0]); +	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_PROTO_BE_MASK, +				match->mask.vlan_proto[0]); +	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_TCI_BE, +				match->value.vlan_tci[1]); +	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_TCI_BE_MASK, +				match->mask.vlan_tci[1]); +	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_PROTO_BE, +				match->value.vlan_proto[1]); +	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_PROTO_BE_MASK, +				match->mask.vlan_proto[1]); +	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_SADDR_BE), +	       match->value.eth_saddr, ETH_ALEN); +	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_SADDR_BE_MASK), +	       match->mask.eth_saddr, ETH_ALEN); +	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_DADDR_BE), +	       match->value.eth_daddr, ETH_ALEN); +	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_DADDR_BE_MASK), +	       match->mask.eth_daddr, ETH_ALEN); +	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_PROTO, +			     match->value.ip_proto); +	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_PROTO_MASK, +			     match->mask.ip_proto); +	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TOS, +			     match->value.ip_tos); +	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TOS_MASK, +			     match->mask.ip_tos); +	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TTL, +			     match->value.ip_ttl); +	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TTL_MASK, +			     match->mask.ip_ttl); +	MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP4_BE, +				 match->value.src_ip); +	MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP4_BE_MASK, +				 match->mask.src_ip); +	MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP4_BE, +				 match->value.dst_ip); +	MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP4_BE_MASK, +				 match->mask.dst_ip); +#ifdef CONFIG_IPV6 +	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP6_BE), +	       &match->value.src_ip6, sizeof(struct in6_addr)); +	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP6_BE_MASK), +	       &match->mask.src_ip6, sizeof(struct in6_addr)); +	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP6_BE), +	       &match->value.dst_ip6, sizeof(struct in6_addr)); +	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP6_BE_MASK), +	       &match->mask.dst_ip6, sizeof(struct in6_addr)); +#endif +	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_SPORT_BE, +				match->value.l4_sport); +	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_SPORT_BE_MASK, +				match->mask.l4_sport); +	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_DPORT_BE, +				match->value.l4_dport); +	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_DPORT_BE_MASK, +				match->mask.l4_dport); +	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE, +				match->value.tcp_flags); +	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE_MASK, +				match->mask.tcp_flags);  	return 0;  } diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h index 1f18e9dc62e8..fbeb58104936 100644 --- a/drivers/net/ethernet/sfc/mcdi.h +++ b/drivers/net/ethernet/sfc/mcdi.h @@ -224,12 +224,24 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev);  #define MCDI_WORD(_buf, _field)						\  	((u16)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2) +	\  	 le16_to_cpu(*(__force const __le16 *)MCDI_PTR(_buf, _field))) +/* Write a 16-bit field defined in the protocol as being big-endian. */ +#define MCDI_STRUCT_SET_WORD_BE(_buf, _field, _value) do {		\ +	BUILD_BUG_ON(_field ## _LEN != 2);				\ +	BUILD_BUG_ON(_field ## _OFST & 1);				\ +	*(__force __be16 *)MCDI_STRUCT_PTR(_buf, _field) = (_value);	\ +	} while (0)  #define MCDI_SET_DWORD(_buf, _field, _value)				\  	EFX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field), EFX_DWORD_0, _value)  #define MCDI_STRUCT_SET_DWORD(_buf, _field, _value)			\  	EFX_POPULATE_DWORD_1(*_MCDI_STRUCT_DWORD(_buf, _field), EFX_DWORD_0, _value)  #define MCDI_DWORD(_buf, _field)					\  	EFX_DWORD_FIELD(*_MCDI_DWORD(_buf, _field), EFX_DWORD_0) +/* Write a 32-bit field defined in the protocol as being big-endian. */ +#define MCDI_STRUCT_SET_DWORD_BE(_buf, _field, _value) do {		\ +	BUILD_BUG_ON(_field ## _LEN != 4);				\ +	BUILD_BUG_ON(_field ## _OFST & 3);				\ +	*(__force __be32 *)MCDI_STRUCT_PTR(_buf, _field) = (_value);	\ +	} while (0)  #define MCDI_POPULATE_DWORD_1(_buf, _field, _name1, _value1)		\  	EFX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field),		\  			     MC_CMD_ ## _name1, _value1) diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c index b21a961eabb1..17e1a3447554 100644 --- a/drivers/net/ethernet/sfc/tc.c +++ b/drivers/net/ethernet/sfc/tc.c @@ -124,19 +124,59 @@ static void efx_tc_flow_free(void *ptr, void *arg)  	kfree(rule);  } +/* Boilerplate for the simple 'copy a field' cases */ +#define _MAP_KEY_AND_MASK(_name, _type, _tcget, _tcfield, _field)	\ +if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_##_name)) {		\ +	struct flow_match_##_type fm;					\ +									\ +	flow_rule_match_##_tcget(rule, &fm);				\ +	match->value._field = fm.key->_tcfield;				\ +	match->mask._field = fm.mask->_tcfield;				\ +} +#define MAP_KEY_AND_MASK(_name, _type, _tcfield, _field)	\ +	_MAP_KEY_AND_MASK(_name, _type, _type, _tcfield, _field) +#define MAP_ENC_KEY_AND_MASK(_name, _type, _tcget, _tcfield, _field)	\ +	_MAP_KEY_AND_MASK(ENC_##_name, _type, _tcget, _tcfield, _field) +  static int efx_tc_flower_parse_match(struct efx_nic *efx,  				     struct flow_rule *rule,  				     struct efx_tc_match *match,  				     struct netlink_ext_ack *extack)  {  	struct flow_dissector *dissector = rule->match.dissector; +	unsigned char ipv = 0; +	/* Owing to internal TC infelicities, the IPV6_ADDRS key might be set +	 * even on IPv4 filters; so rather than relying on dissector->used_keys +	 * we check the addr_type in the CONTROL key.  If we don't find it (or +	 * it's masked, which should never happen), we treat both IPV4_ADDRS +	 * and IPV6_ADDRS as absent. +	 */  	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {  		struct flow_match_control fm;  		flow_rule_match_control(rule, &fm); +		if (IS_ALL_ONES(fm.mask->addr_type)) +			switch (fm.key->addr_type) { +			case FLOW_DISSECTOR_KEY_IPV4_ADDRS: +				ipv = 4; +				break; +			case FLOW_DISSECTOR_KEY_IPV6_ADDRS: +				ipv = 6; +				break; +			default: +				break; +			} -		if (fm.mask->flags) { +		if (fm.mask->flags & FLOW_DIS_IS_FRAGMENT) { +			match->value.ip_frag = fm.key->flags & FLOW_DIS_IS_FRAGMENT; +			match->mask.ip_frag = true; +		} +		if (fm.mask->flags & FLOW_DIS_FIRST_FRAG) { +			match->value.ip_firstfrag = fm.key->flags & FLOW_DIS_FIRST_FRAG; +			match->mask.ip_firstfrag = true; +		} +		if (fm.mask->flags & ~(FLOW_DIS_IS_FRAGMENT | FLOW_DIS_FIRST_FRAG)) {  			NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported match on control.flags %#x",  					       fm.mask->flags);  			return -EOPNOTSUPP; @@ -144,26 +184,101 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx,  	}  	if (dissector->used_keys &  	    ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) | -	      BIT(FLOW_DISSECTOR_KEY_BASIC))) { +	      BIT(FLOW_DISSECTOR_KEY_BASIC) | +	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) | +	      BIT(FLOW_DISSECTOR_KEY_VLAN) | +	      BIT(FLOW_DISSECTOR_KEY_CVLAN) | +	      BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | +	      BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | +	      BIT(FLOW_DISSECTOR_KEY_PORTS) | +	      BIT(FLOW_DISSECTOR_KEY_TCP) | +	      BIT(FLOW_DISSECTOR_KEY_IP))) {  		NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported flower keys %#x",  				       dissector->used_keys);  		return -EOPNOTSUPP;  	} -	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { -		struct flow_match_basic fm; +	MAP_KEY_AND_MASK(BASIC, basic, n_proto, eth_proto); +	/* Make sure we're IP if any L3/L4 keys used. */ +	if (!IS_ALL_ONES(match->mask.eth_proto) || +	    !(match->value.eth_proto == htons(ETH_P_IP) || +	      match->value.eth_proto == htons(ETH_P_IPV6))) +		if (dissector->used_keys & +		    (BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | +		     BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | +		     BIT(FLOW_DISSECTOR_KEY_PORTS) | +		     BIT(FLOW_DISSECTOR_KEY_IP) | +		     BIT(FLOW_DISSECTOR_KEY_TCP))) { +			NL_SET_ERR_MSG_FMT_MOD(extack, "L3/L4 flower keys %#x require protocol ipv[46]", +					       dissector->used_keys); +			return -EINVAL; +		} -		flow_rule_match_basic(rule, &fm); -		if (fm.mask->n_proto) { -			NL_SET_ERR_MSG_MOD(extack, "Unsupported eth_proto match"); -			return -EOPNOTSUPP; +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) { +		struct flow_match_vlan fm; + +		flow_rule_match_vlan(rule, &fm); +		if (fm.mask->vlan_id || fm.mask->vlan_priority || fm.mask->vlan_tpid) { +			match->value.vlan_proto[0] = fm.key->vlan_tpid; +			match->mask.vlan_proto[0] = fm.mask->vlan_tpid; +			match->value.vlan_tci[0] = cpu_to_be16(fm.key->vlan_priority << 13 | +							       fm.key->vlan_id); +			match->mask.vlan_tci[0] = cpu_to_be16(fm.mask->vlan_priority << 13 | +							      fm.mask->vlan_id);  		} -		if (fm.mask->ip_proto) { -			NL_SET_ERR_MSG_MOD(extack, "Unsupported ip_proto match"); -			return -EOPNOTSUPP; +	} + +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CVLAN)) { +		struct flow_match_vlan fm; + +		flow_rule_match_cvlan(rule, &fm); +		if (fm.mask->vlan_id || fm.mask->vlan_priority || fm.mask->vlan_tpid) { +			match->value.vlan_proto[1] = fm.key->vlan_tpid; +			match->mask.vlan_proto[1] = fm.mask->vlan_tpid; +			match->value.vlan_tci[1] = cpu_to_be16(fm.key->vlan_priority << 13 | +							       fm.key->vlan_id); +			match->mask.vlan_tci[1] = cpu_to_be16(fm.mask->vlan_priority << 13 | +							      fm.mask->vlan_id);  		}  	} +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { +		struct flow_match_eth_addrs fm; + +		flow_rule_match_eth_addrs(rule, &fm); +		ether_addr_copy(match->value.eth_saddr, fm.key->src); +		ether_addr_copy(match->value.eth_daddr, fm.key->dst); +		ether_addr_copy(match->mask.eth_saddr, fm.mask->src); +		ether_addr_copy(match->mask.eth_daddr, fm.mask->dst); +	} + +	MAP_KEY_AND_MASK(BASIC, basic, ip_proto, ip_proto); +	/* Make sure we're TCP/UDP if any L4 keys used. */ +	if ((match->value.ip_proto != IPPROTO_UDP && +	     match->value.ip_proto != IPPROTO_TCP) || !IS_ALL_ONES(match->mask.ip_proto)) +		if (dissector->used_keys & +		    (BIT(FLOW_DISSECTOR_KEY_PORTS) | +		     BIT(FLOW_DISSECTOR_KEY_TCP))) { +			NL_SET_ERR_MSG_FMT_MOD(extack, "L4 flower keys %#x require ipproto udp or tcp", +					       dissector->used_keys); +			return -EINVAL; +		} +	MAP_KEY_AND_MASK(IP, ip, tos, ip_tos); +	MAP_KEY_AND_MASK(IP, ip, ttl, ip_ttl); +	if (ipv == 4) { +		MAP_KEY_AND_MASK(IPV4_ADDRS, ipv4_addrs, src, src_ip); +		MAP_KEY_AND_MASK(IPV4_ADDRS, ipv4_addrs, dst, dst_ip); +	} +#ifdef CONFIG_IPV6 +	else if (ipv == 6) { +		MAP_KEY_AND_MASK(IPV6_ADDRS, ipv6_addrs, src, src_ip6); +		MAP_KEY_AND_MASK(IPV6_ADDRS, ipv6_addrs, dst, dst_ip6); +	} +#endif +	MAP_KEY_AND_MASK(PORTS, ports, src, l4_sport); +	MAP_KEY_AND_MASK(PORTS, ports, dst, l4_dport); +	MAP_KEY_AND_MASK(TCP, tcp, flags, tcp_flags); +  	return 0;  } diff --git a/drivers/net/ethernet/sfc/tc.h b/drivers/net/ethernet/sfc/tc.h index 4373c3243e3c..4240c375a8e6 100644 --- a/drivers/net/ethernet/sfc/tc.h +++ b/drivers/net/ethernet/sfc/tc.h @@ -15,6 +15,8 @@  #include <linux/rhashtable.h>  #include "net_driver.h" +#define IS_ALL_ONES(v)	(!(typeof (v))~(v)) +  struct efx_tc_action_set {  	u16 deliver:1;  	u32 dest_mport; @@ -26,6 +28,20 @@ struct efx_tc_match_fields {  	/* L1 */  	u32 ingress_port;  	u8 recirc_id; +	/* L2 (inner when encap) */ +	__be16 eth_proto; +	__be16 vlan_tci[2], vlan_proto[2]; +	u8 eth_saddr[ETH_ALEN], eth_daddr[ETH_ALEN]; +	/* L3 (when IP) */ +	u8 ip_proto, ip_tos, ip_ttl; +	__be32 src_ip, dst_ip; +#ifdef CONFIG_IPV6 +	struct in6_addr src_ip6, dst_ip6; +#endif +	bool ip_frag, ip_firstfrag; +	/* L4 */ +	__be16 l4_sport, l4_dport; /* Ports (UDP, TCP) */ +	__be16 tcp_flags;  };  struct efx_tc_match { | 
