Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'sfc-tc-decap-support'

Edward Cree says:

====================
sfc: support TC decap rules

This series adds support for offloading tunnel decapsulation TC rules to
ef100 NICs, allowing matching encapsulated packets to be decapsulated in
hardware and redirected to VFs.
For now an encap match must be on precisely the following fields:
ethertype (IPv4 or IPv6), source IP, destination IP, ipproto UDP,
UDP destination port. This simplifies checking for overlaps in the
driver; the hardware supports a wider range of match fields which
future driver work may expose.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>

+857 -18
+223 -4
drivers/net/ethernet/sfc/mae.c
··· 241 241 if (outlen < sizeof(outbuf)) 242 242 return -EIO; 243 243 caps->match_field_count = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_MATCH_FIELD_COUNT); 244 + caps->encap_types = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_ENCAP_TYPES_SUPPORTED); 244 245 caps->action_prios = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_ACTION_PRIOS); 245 246 return 0; 246 247 } ··· 255 254 size_t outlen; 256 255 int rc, i; 257 256 257 + /* AR and OR caps MCDIs have identical layout, so we are using the 258 + * same code for both. 259 + */ 260 + BUILD_BUG_ON(MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(MAE_NUM_FIELDS) < 261 + MC_CMD_MAE_GET_OR_CAPS_OUT_LEN(MAE_NUM_FIELDS)); 258 262 BUILD_BUG_ON(MC_CMD_MAE_GET_AR_CAPS_IN_LEN); 263 + BUILD_BUG_ON(MC_CMD_MAE_GET_OR_CAPS_IN_LEN); 259 264 260 265 rc = efx_mcdi_rpc(efx, cmd, NULL, 0, outbuf, sizeof(outbuf), &outlen); 261 266 if (rc) 262 267 return rc; 268 + BUILD_BUG_ON(MC_CMD_MAE_GET_AR_CAPS_OUT_COUNT_OFST != 269 + MC_CMD_MAE_GET_OR_CAPS_OUT_COUNT_OFST); 263 270 count = MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_COUNT); 264 271 memset(field_support, MAE_FIELD_UNSUPPORTED, MAE_NUM_FIELDS); 272 + BUILD_BUG_ON(MC_CMD_MAE_GET_AR_CAPS_OUT_FIELD_FLAGS_OFST != 273 + MC_CMD_MAE_GET_OR_CAPS_OUT_FIELD_FLAGS_OFST); 265 274 caps = _MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_FIELD_FLAGS); 266 275 /* We're only interested in the support status enum, not any other 267 276 * flags, so just extract that from each entry. ··· 289 278 rc = efx_mae_get_basic_caps(efx, caps); 290 279 if (rc) 291 280 return rc; 292 - return efx_mae_get_rule_fields(efx, MC_CMD_MAE_GET_AR_CAPS, 293 - caps->action_rule_fields); 281 + rc = efx_mae_get_rule_fields(efx, MC_CMD_MAE_GET_AR_CAPS, 282 + caps->action_rule_fields); 283 + if (rc) 284 + return rc; 285 + return efx_mae_get_rule_fields(efx, MC_CMD_MAE_GET_OR_CAPS, 286 + caps->outer_rule_fields); 294 287 } 295 288 296 289 /* Bit twiddling: ··· 447 432 CHECK_BIT(IP_FIRST_FRAG, ip_firstfrag) || 448 433 CHECK(RECIRC_ID, recirc_id)) 449 434 return rc; 435 + /* Matches on outer fields are done in a separate hardware table, 436 + * the Outer Rule table. Thus the Action Rule merely does an 437 + * exact match on Outer Rule ID if any outer field matches are 438 + * present. The exception is the VNI/VSID (enc_keyid), which is 439 + * available to the Action Rule match iff the Outer Rule matched 440 + * (and thus identified the encap protocol to use to extract it). 441 + */ 442 + if (efx_tc_match_is_encap(mask)) { 443 + rc = efx_mae_match_check_cap_typ( 444 + supported_fields[MAE_FIELD_OUTER_RULE_ID], 445 + MASK_ONES); 446 + if (rc) { 447 + NL_SET_ERR_MSG_MOD(extack, "No support for encap rule ID matches"); 448 + return rc; 449 + } 450 + if (CHECK(ENC_VNET_ID, enc_keyid)) 451 + return rc; 452 + } else if (mask->enc_keyid) { 453 + NL_SET_ERR_MSG_MOD(extack, "Match on enc_keyid requires other encap fields"); 454 + return -EINVAL; 455 + } 450 456 return 0; 451 457 } 452 458 #undef CHECK_BIT 453 459 #undef CHECK 460 + 461 + #define CHECK(_mcdi) ({ \ 462 + rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ ## _mcdi],\ 463 + MASK_ONES); \ 464 + if (rc) \ 465 + NL_SET_ERR_MSG_FMT_MOD(extack, \ 466 + "No support for field %s", #_mcdi); \ 467 + rc; \ 468 + }) 469 + /* Checks that the fields needed for encap-rule matches are supported by the 470 + * MAE. All the fields are exact-match. 471 + */ 472 + int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6, 473 + struct netlink_ext_ack *extack) 474 + { 475 + u8 *supported_fields = efx->tc->caps->outer_rule_fields; 476 + int rc; 477 + 478 + if (CHECK(ENC_ETHER_TYPE)) 479 + return rc; 480 + if (ipv6) { 481 + if (CHECK(ENC_SRC_IP6) || 482 + CHECK(ENC_DST_IP6)) 483 + return rc; 484 + } else { 485 + if (CHECK(ENC_SRC_IP4) || 486 + CHECK(ENC_DST_IP4)) 487 + return rc; 488 + } 489 + if (CHECK(ENC_L4_DPORT) || 490 + CHECK(ENC_IP_PROTO)) 491 + return rc; 492 + return 0; 493 + } 494 + #undef CHECK 495 + 496 + int efx_mae_check_encap_type_supported(struct efx_nic *efx, enum efx_encap_type typ) 497 + { 498 + unsigned int bit; 499 + 500 + switch (typ & EFX_ENCAP_TYPES_MASK) { 501 + case EFX_ENCAP_TYPE_VXLAN: 502 + bit = MC_CMD_MAE_GET_CAPS_OUT_ENCAP_TYPE_VXLAN_LBN; 503 + break; 504 + case EFX_ENCAP_TYPE_GENEVE: 505 + bit = MC_CMD_MAE_GET_CAPS_OUT_ENCAP_TYPE_GENEVE_LBN; 506 + break; 507 + default: 508 + return -EOPNOTSUPP; 509 + } 510 + if (efx->tc->caps->encap_types & BIT(bit)) 511 + return 0; 512 + return -EOPNOTSUPP; 513 + } 454 514 455 515 int efx_mae_allocate_counter(struct efx_nic *efx, struct efx_tc_counter *cnt) 456 516 { ··· 576 486 cnt->fw_id)) 577 487 return -EIO; 578 488 return 0; 489 + } 490 + 491 + static int efx_mae_encap_type_to_mae_type(enum efx_encap_type type) 492 + { 493 + switch (type & EFX_ENCAP_TYPES_MASK) { 494 + case EFX_ENCAP_TYPE_NONE: 495 + return MAE_MCDI_ENCAP_TYPE_NONE; 496 + case EFX_ENCAP_TYPE_VXLAN: 497 + return MAE_MCDI_ENCAP_TYPE_VXLAN; 498 + case EFX_ENCAP_TYPE_GENEVE: 499 + return MAE_MCDI_ENCAP_TYPE_GENEVE; 500 + default: 501 + return -EOPNOTSUPP; 502 + } 579 503 } 580 504 581 505 int efx_mae_lookup_mport(struct efx_nic *efx, u32 vf_idx, u32 *id) ··· 786 682 size_t outlen; 787 683 int rc; 788 684 789 - MCDI_POPULATE_DWORD_2(inbuf, MAE_ACTION_SET_ALLOC_IN_FLAGS, 685 + MCDI_POPULATE_DWORD_3(inbuf, MAE_ACTION_SET_ALLOC_IN_FLAGS, 790 686 MAE_ACTION_SET_ALLOC_IN_VLAN_PUSH, act->vlan_push, 791 - MAE_ACTION_SET_ALLOC_IN_VLAN_POP, act->vlan_pop); 687 + MAE_ACTION_SET_ALLOC_IN_VLAN_POP, act->vlan_pop, 688 + MAE_ACTION_SET_ALLOC_IN_DECAP, act->decap); 792 689 793 690 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID, 794 691 MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL); ··· 950 845 return 0; 951 846 } 952 847 848 + int efx_mae_register_encap_match(struct efx_nic *efx, 849 + struct efx_tc_encap_match *encap) 850 + { 851 + MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_OUTER_RULE_INSERT_IN_LEN(MAE_ENC_FIELD_PAIRS_LEN)); 852 + MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_OUTER_RULE_INSERT_OUT_LEN); 853 + MCDI_DECLARE_STRUCT_PTR(match_crit); 854 + size_t outlen; 855 + int rc; 856 + 857 + rc = efx_mae_encap_type_to_mae_type(encap->tun_type); 858 + if (rc < 0) 859 + return rc; 860 + match_crit = _MCDI_DWORD(inbuf, MAE_OUTER_RULE_INSERT_IN_FIELD_MATCH_CRITERIA); 861 + /* The struct contains IP src and dst, and udp dport. 862 + * So we actually need to filter on IP src and dst, L4 dport, and 863 + * ipproto == udp. 864 + */ 865 + MCDI_SET_DWORD(inbuf, MAE_OUTER_RULE_INSERT_IN_ENCAP_TYPE, rc); 866 + #ifdef CONFIG_IPV6 867 + if (encap->src_ip | encap->dst_ip) { 868 + #endif 869 + MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_SRC_IP4_BE, 870 + encap->src_ip); 871 + MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_SRC_IP4_BE_MASK, 872 + ~(__be32)0); 873 + MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_DST_IP4_BE, 874 + encap->dst_ip); 875 + MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_DST_IP4_BE_MASK, 876 + ~(__be32)0); 877 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETHER_TYPE_BE, 878 + htons(ETH_P_IP)); 879 + #ifdef CONFIG_IPV6 880 + } else { 881 + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_SRC_IP6_BE), 882 + &encap->src_ip6, sizeof(encap->src_ip6)); 883 + memset(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_SRC_IP6_BE_MASK), 884 + 0xff, sizeof(encap->src_ip6)); 885 + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_DST_IP6_BE), 886 + &encap->dst_ip6, sizeof(encap->dst_ip6)); 887 + memset(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_DST_IP6_BE_MASK), 888 + 0xff, sizeof(encap->dst_ip6)); 889 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETHER_TYPE_BE, 890 + htons(ETH_P_IPV6)); 891 + } 892 + #endif 893 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETHER_TYPE_BE_MASK, 894 + ~(__be16)0); 895 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE, 896 + encap->udp_dport); 897 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE_MASK, 898 + ~(__be16)0); 899 + MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO, IPPROTO_UDP); 900 + MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO_MASK, ~0); 901 + rc = efx_mcdi_rpc(efx, MC_CMD_MAE_OUTER_RULE_INSERT, inbuf, 902 + sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); 903 + if (rc) 904 + return rc; 905 + if (outlen < sizeof(outbuf)) 906 + return -EIO; 907 + encap->fw_id = MCDI_DWORD(outbuf, MAE_OUTER_RULE_INSERT_OUT_OR_ID); 908 + return 0; 909 + } 910 + 911 + int efx_mae_unregister_encap_match(struct efx_nic *efx, 912 + struct efx_tc_encap_match *encap) 913 + { 914 + MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_OUTER_RULE_REMOVE_OUT_LEN(1)); 915 + MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_OUTER_RULE_REMOVE_IN_LEN(1)); 916 + size_t outlen; 917 + int rc; 918 + 919 + MCDI_SET_DWORD(inbuf, MAE_OUTER_RULE_REMOVE_IN_OR_ID, encap->fw_id); 920 + rc = efx_mcdi_rpc(efx, MC_CMD_MAE_OUTER_RULE_REMOVE, inbuf, 921 + sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); 922 + if (rc) 923 + return rc; 924 + if (outlen < sizeof(outbuf)) 925 + return -EIO; 926 + /* FW freed a different ID than we asked for, should also never happen. 927 + * Warn because it means we've now got a different idea to the FW of 928 + * what encap_mds exist, which could cause mayhem later. 929 + */ 930 + if (WARN_ON(MCDI_DWORD(outbuf, MAE_OUTER_RULE_REMOVE_OUT_REMOVED_OR_ID) != encap->fw_id)) 931 + return -EIO; 932 + /* We're probably about to free @encap, but let's just make sure its 933 + * fw_id is blatted so that it won't look valid if it leaks out. 934 + */ 935 + encap->fw_id = MC_CMD_MAE_OUTER_RULE_INSERT_OUT_OUTER_RULE_ID_NULL; 936 + return 0; 937 + } 938 + 953 939 static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit), 954 940 const struct efx_tc_match *match) 955 941 { ··· 1137 941 match->value.tcp_flags); 1138 942 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE_MASK, 1139 943 match->mask.tcp_flags); 944 + /* enc-keys are handled indirectly, through encap_match ID */ 945 + if (match->encap) { 946 + MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_OUTER_RULE_ID, 947 + match->encap->fw_id); 948 + MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_OUTER_RULE_ID_MASK, 949 + U32_MAX); 950 + /* enc_keyid (VNI/VSID) is not part of the encap_match */ 951 + MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ENC_VNET_ID_BE, 952 + match->value.enc_keyid); 953 + MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ENC_VNET_ID_BE_MASK, 954 + match->mask.enc_keyid); 955 + } else if (WARN_ON_ONCE(match->mask.enc_src_ip) || 956 + WARN_ON_ONCE(match->mask.enc_dst_ip) || 957 + WARN_ON_ONCE(!ipv6_addr_any(&match->mask.enc_src_ip6)) || 958 + WARN_ON_ONCE(!ipv6_addr_any(&match->mask.enc_dst_ip6)) || 959 + WARN_ON_ONCE(match->mask.enc_ip_tos) || 960 + WARN_ON_ONCE(match->mask.enc_ip_ttl) || 961 + WARN_ON_ONCE(match->mask.enc_sport) || 962 + WARN_ON_ONCE(match->mask.enc_dport) || 963 + WARN_ON_ONCE(match->mask.enc_keyid)) { 964 + /* No enc-keys should appear in a rule without an encap_match */ 965 + return -EOPNOTSUPP; 966 + } 1140 967 return 0; 1141 968 } 1142 969
+11
drivers/net/ethernet/sfc/mae.h
··· 70 70 71 71 struct mae_caps { 72 72 u32 match_field_count; 73 + u32 encap_types; 73 74 u32 action_prios; 74 75 u8 action_rule_fields[MAE_NUM_FIELDS]; 76 + u8 outer_rule_fields[MAE_NUM_FIELDS]; 75 77 }; 76 78 77 79 int efx_mae_get_caps(struct efx_nic *efx, struct mae_caps *caps); ··· 81 79 int efx_mae_match_check_caps(struct efx_nic *efx, 82 80 const struct efx_tc_match_fields *mask, 83 81 struct netlink_ext_ack *extack); 82 + int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6, 83 + struct netlink_ext_ack *extack); 84 + int efx_mae_check_encap_type_supported(struct efx_nic *efx, 85 + enum efx_encap_type typ); 84 86 85 87 int efx_mae_allocate_counter(struct efx_nic *efx, struct efx_tc_counter *cnt); 86 88 int efx_mae_free_counter(struct efx_nic *efx, struct efx_tc_counter *cnt); ··· 96 90 struct efx_tc_action_set_list *acts); 97 91 int efx_mae_free_action_set_list(struct efx_nic *efx, 98 92 struct efx_tc_action_set_list *acts); 93 + 94 + int efx_mae_register_encap_match(struct efx_nic *efx, 95 + struct efx_tc_encap_match *encap); 96 + int efx_mae_unregister_encap_match(struct efx_nic *efx, 97 + struct efx_tc_encap_match *encap); 99 98 100 99 int efx_mae_insert_rule(struct efx_nic *efx, const struct efx_tc_match *match, 101 100 u32 prio, u32 acts_id, u32 *id);
+586 -14
drivers/net/ethernet/sfc/tc.c
··· 10 10 */ 11 11 12 12 #include <net/pkt_cls.h> 13 + #include <net/vxlan.h> 14 + #include <net/geneve.h> 13 15 #include "tc.h" 14 16 #include "tc_bindings.h" 15 17 #include "mae.h" 16 18 #include "ef100_rep.h" 17 19 #include "efx.h" 20 + 21 + static enum efx_encap_type efx_tc_indr_netdev_type(struct net_device *net_dev) 22 + { 23 + if (netif_is_vxlan(net_dev)) 24 + return EFX_ENCAP_TYPE_VXLAN; 25 + if (netif_is_geneve(net_dev)) 26 + return EFX_ENCAP_TYPE_GENEVE; 27 + 28 + return EFX_ENCAP_TYPE_NONE; 29 + } 18 30 19 31 #define EFX_EFV_PF NULL 20 32 /* Look up the representor information (efv) for a device. ··· 55 43 return efv; 56 44 } 57 45 46 + /* Convert a driver-internal vport ID into an internal device (PF or VF) */ 47 + static s64 efx_tc_flower_internal_mport(struct efx_nic *efx, struct efx_rep *efv) 48 + { 49 + u32 mport; 50 + 51 + if (IS_ERR(efv)) 52 + return PTR_ERR(efv); 53 + if (!efv) /* device is PF (us) */ 54 + efx_mae_mport_uplink(efx, &mport); 55 + else /* device is repr */ 56 + efx_mae_mport_mport(efx, efv->mport, &mport); 57 + return mport; 58 + } 59 + 58 60 /* Convert a driver-internal vport ID into an external device (wire or VF) */ 59 61 static s64 efx_tc_flower_external_mport(struct efx_nic *efx, struct efx_rep *efv) 60 62 { ··· 83 57 return mport; 84 58 } 85 59 60 + static const struct rhashtable_params efx_tc_encap_match_ht_params = { 61 + .key_len = offsetof(struct efx_tc_encap_match, linkage), 62 + .key_offset = 0, 63 + .head_offset = offsetof(struct efx_tc_encap_match, linkage), 64 + }; 65 + 86 66 static const struct rhashtable_params efx_tc_match_action_ht_params = { 87 67 .key_len = sizeof(unsigned long), 88 68 .key_offset = offsetof(struct efx_tc_flow_rule, cookie), ··· 98 66 static void efx_tc_free_action_set(struct efx_nic *efx, 99 67 struct efx_tc_action_set *act, bool in_hw) 100 68 { 101 - /* Failure paths calling this on the 'running action' set in_hw=false, 69 + /* Failure paths calling this on the 'cursor' action set in_hw=false, 102 70 * because if the alloc had succeeded we'd've put it in acts.list and 103 71 * not still have it in act. 104 72 */ ··· 130 98 list_for_each_entry_safe(act, next, &acts->list, list) 131 99 efx_tc_free_action_set(efx, act, true); 132 100 /* Don't kfree, as acts is embedded inside a struct efx_tc_flow_rule */ 133 - } 134 - 135 - static void efx_tc_delete_rule(struct efx_nic *efx, struct efx_tc_flow_rule *rule) 136 - { 137 - efx_mae_delete_rule(efx, rule->fw_id); 138 - 139 - /* Release entries in subsidiary tables */ 140 - efx_tc_free_action_set_list(efx, &rule->acts, true); 141 - rule->fw_id = MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL; 142 101 } 143 102 144 103 static void efx_tc_flow_free(void *ptr, void *arg) ··· 216 193 BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | 217 194 BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | 218 195 BIT(FLOW_DISSECTOR_KEY_PORTS) | 196 + BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) | 197 + BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) | 198 + BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) | 199 + BIT(FLOW_DISSECTOR_KEY_ENC_PORTS) | 200 + BIT(FLOW_DISSECTOR_KEY_ENC_CONTROL) | 219 201 BIT(FLOW_DISSECTOR_KEY_TCP) | 220 202 BIT(FLOW_DISSECTOR_KEY_IP))) { 221 203 NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported flower keys %#x", ··· 308 280 MAP_KEY_AND_MASK(PORTS, ports, src, l4_sport); 309 281 MAP_KEY_AND_MASK(PORTS, ports, dst, l4_dport); 310 282 MAP_KEY_AND_MASK(TCP, tcp, flags, tcp_flags); 283 + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_CONTROL)) { 284 + struct flow_match_control fm; 285 + 286 + flow_rule_match_enc_control(rule, &fm); 287 + if (fm.mask->flags) { 288 + NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported match on enc_control.flags %#x", 289 + fm.mask->flags); 290 + return -EOPNOTSUPP; 291 + } 292 + if (!IS_ALL_ONES(fm.mask->addr_type)) { 293 + NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported enc addr_type mask %u (key %u)", 294 + fm.mask->addr_type, 295 + fm.key->addr_type); 296 + return -EOPNOTSUPP; 297 + } 298 + switch (fm.key->addr_type) { 299 + case FLOW_DISSECTOR_KEY_IPV4_ADDRS: 300 + MAP_ENC_KEY_AND_MASK(IPV4_ADDRS, ipv4_addrs, enc_ipv4_addrs, 301 + src, enc_src_ip); 302 + MAP_ENC_KEY_AND_MASK(IPV4_ADDRS, ipv4_addrs, enc_ipv4_addrs, 303 + dst, enc_dst_ip); 304 + break; 305 + #ifdef CONFIG_IPV6 306 + case FLOW_DISSECTOR_KEY_IPV6_ADDRS: 307 + MAP_ENC_KEY_AND_MASK(IPV6_ADDRS, ipv6_addrs, enc_ipv6_addrs, 308 + src, enc_src_ip6); 309 + MAP_ENC_KEY_AND_MASK(IPV6_ADDRS, ipv6_addrs, enc_ipv6_addrs, 310 + dst, enc_dst_ip6); 311 + break; 312 + #endif 313 + default: 314 + NL_SET_ERR_MSG_FMT_MOD(extack, 315 + "Unsupported enc addr_type %u (supported are IPv4, IPv6)", 316 + fm.key->addr_type); 317 + return -EOPNOTSUPP; 318 + } 319 + MAP_ENC_KEY_AND_MASK(IP, ip, enc_ip, tos, enc_ip_tos); 320 + MAP_ENC_KEY_AND_MASK(IP, ip, enc_ip, ttl, enc_ip_ttl); 321 + MAP_ENC_KEY_AND_MASK(PORTS, ports, enc_ports, src, enc_sport); 322 + MAP_ENC_KEY_AND_MASK(PORTS, ports, enc_ports, dst, enc_dport); 323 + MAP_ENC_KEY_AND_MASK(KEYID, enc_keyid, enc_keyid, keyid, enc_keyid); 324 + } else if (dissector->used_keys & 325 + (BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) | 326 + BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) | 327 + BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) | 328 + BIT(FLOW_DISSECTOR_KEY_ENC_IP) | 329 + BIT(FLOW_DISSECTOR_KEY_ENC_PORTS))) { 330 + NL_SET_ERR_MSG_FMT_MOD(extack, "Flower enc keys require enc_control (keys: %#x)", 331 + dissector->used_keys); 332 + return -EOPNOTSUPP; 333 + } 311 334 312 335 return 0; 313 336 } 314 337 338 + static int efx_tc_flower_record_encap_match(struct efx_nic *efx, 339 + struct efx_tc_match *match, 340 + enum efx_encap_type type, 341 + struct netlink_ext_ack *extack) 342 + { 343 + struct efx_tc_encap_match *encap, *old; 344 + bool ipv6 = false; 345 + int rc; 346 + 347 + /* We require that the socket-defining fields (IP addrs and UDP dest 348 + * port) are present and exact-match. Other fields are currently not 349 + * allowed. This meets what OVS will ask for, and means that we don't 350 + * need to handle difficult checks for overlapping matches as could 351 + * come up if we allowed masks or varying sets of match fields. 352 + */ 353 + if (match->mask.enc_dst_ip | match->mask.enc_src_ip) { 354 + if (!IS_ALL_ONES(match->mask.enc_dst_ip)) { 355 + NL_SET_ERR_MSG_MOD(extack, 356 + "Egress encap match is not exact on dst IP address"); 357 + return -EOPNOTSUPP; 358 + } 359 + if (!IS_ALL_ONES(match->mask.enc_src_ip)) { 360 + NL_SET_ERR_MSG_MOD(extack, 361 + "Egress encap match is not exact on src IP address"); 362 + return -EOPNOTSUPP; 363 + } 364 + #ifdef CONFIG_IPV6 365 + if (!ipv6_addr_any(&match->mask.enc_dst_ip6) || 366 + !ipv6_addr_any(&match->mask.enc_src_ip6)) { 367 + NL_SET_ERR_MSG_MOD(extack, 368 + "Egress encap match on both IPv4 and IPv6, don't understand"); 369 + return -EOPNOTSUPP; 370 + } 371 + } else { 372 + ipv6 = true; 373 + if (!efx_ipv6_addr_all_ones(&match->mask.enc_dst_ip6)) { 374 + NL_SET_ERR_MSG_MOD(extack, 375 + "Egress encap match is not exact on dst IP address"); 376 + return -EOPNOTSUPP; 377 + } 378 + if (!efx_ipv6_addr_all_ones(&match->mask.enc_src_ip6)) { 379 + NL_SET_ERR_MSG_MOD(extack, 380 + "Egress encap match is not exact on src IP address"); 381 + return -EOPNOTSUPP; 382 + } 383 + #endif 384 + } 385 + if (!IS_ALL_ONES(match->mask.enc_dport)) { 386 + NL_SET_ERR_MSG_MOD(extack, "Egress encap match is not exact on dst UDP port"); 387 + return -EOPNOTSUPP; 388 + } 389 + if (match->mask.enc_sport) { 390 + NL_SET_ERR_MSG_MOD(extack, "Egress encap match on src UDP port not supported"); 391 + return -EOPNOTSUPP; 392 + } 393 + if (match->mask.enc_ip_tos) { 394 + NL_SET_ERR_MSG_MOD(extack, "Egress encap match on IP ToS not supported"); 395 + return -EOPNOTSUPP; 396 + } 397 + if (match->mask.enc_ip_ttl) { 398 + NL_SET_ERR_MSG_MOD(extack, "Egress encap match on IP TTL not supported"); 399 + return -EOPNOTSUPP; 400 + } 401 + 402 + rc = efx_mae_check_encap_match_caps(efx, ipv6, extack); 403 + if (rc) { 404 + NL_SET_ERR_MSG_FMT_MOD(extack, "MAE hw reports no support for IPv%d encap matches", 405 + ipv6 ? 6 : 4); 406 + return -EOPNOTSUPP; 407 + } 408 + 409 + encap = kzalloc(sizeof(*encap), GFP_USER); 410 + if (!encap) 411 + return -ENOMEM; 412 + encap->src_ip = match->value.enc_src_ip; 413 + encap->dst_ip = match->value.enc_dst_ip; 414 + #ifdef CONFIG_IPV6 415 + encap->src_ip6 = match->value.enc_src_ip6; 416 + encap->dst_ip6 = match->value.enc_dst_ip6; 417 + #endif 418 + encap->udp_dport = match->value.enc_dport; 419 + encap->tun_type = type; 420 + old = rhashtable_lookup_get_insert_fast(&efx->tc->encap_match_ht, 421 + &encap->linkage, 422 + efx_tc_encap_match_ht_params); 423 + if (old) { 424 + /* don't need our new entry */ 425 + kfree(encap); 426 + if (old->tun_type != type) { 427 + NL_SET_ERR_MSG_FMT_MOD(extack, 428 + "Egress encap match with conflicting tun_type %u != %u", 429 + old->tun_type, type); 430 + return -EEXIST; 431 + } 432 + if (!refcount_inc_not_zero(&old->ref)) 433 + return -EAGAIN; 434 + /* existing entry found */ 435 + encap = old; 436 + } else { 437 + rc = efx_mae_register_encap_match(efx, encap); 438 + if (rc) { 439 + NL_SET_ERR_MSG_MOD(extack, "Failed to record egress encap match in HW"); 440 + goto fail; 441 + } 442 + refcount_set(&encap->ref, 1); 443 + } 444 + match->encap = encap; 445 + return 0; 446 + fail: 447 + rhashtable_remove_fast(&efx->tc->encap_match_ht, &encap->linkage, 448 + efx_tc_encap_match_ht_params); 449 + kfree(encap); 450 + return rc; 451 + } 452 + 453 + static void efx_tc_flower_release_encap_match(struct efx_nic *efx, 454 + struct efx_tc_encap_match *encap) 455 + { 456 + int rc; 457 + 458 + if (!refcount_dec_and_test(&encap->ref)) 459 + return; /* still in use */ 460 + 461 + rc = efx_mae_unregister_encap_match(efx, encap); 462 + if (rc) 463 + /* Display message but carry on and remove entry from our 464 + * SW tables, because there's not much we can do about it. 465 + */ 466 + netif_err(efx, drv, efx->net_dev, 467 + "Failed to release encap match %#x, rc %d\n", 468 + encap->fw_id, rc); 469 + rhashtable_remove_fast(&efx->tc->encap_match_ht, &encap->linkage, 470 + efx_tc_encap_match_ht_params); 471 + kfree(encap); 472 + } 473 + 474 + static void efx_tc_delete_rule(struct efx_nic *efx, struct efx_tc_flow_rule *rule) 475 + { 476 + efx_mae_delete_rule(efx, rule->fw_id); 477 + 478 + /* Release entries in subsidiary tables */ 479 + efx_tc_free_action_set_list(efx, &rule->acts, true); 480 + if (rule->match.encap) 481 + efx_tc_flower_release_encap_match(efx, rule->match.encap); 482 + rule->fw_id = MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL; 483 + } 484 + 485 + static const char *efx_tc_encap_type_name(enum efx_encap_type typ) 486 + { 487 + switch (typ) { 488 + case EFX_ENCAP_TYPE_NONE: 489 + return "none"; 490 + case EFX_ENCAP_TYPE_VXLAN: 491 + return "vxlan"; 492 + case EFX_ENCAP_TYPE_GENEVE: 493 + return "geneve"; 494 + default: 495 + pr_warn_once("Unknown efx_encap_type %d encountered\n", typ); 496 + return "unknown"; 497 + } 498 + } 499 + 315 500 /* For details of action order constraints refer to SF-123102-TC-1§12.6.1 */ 316 501 enum efx_tc_action_order { 502 + EFX_TC_AO_DECAP, 317 503 EFX_TC_AO_VLAN_POP, 318 504 EFX_TC_AO_VLAN_PUSH, 319 505 EFX_TC_AO_COUNT, ··· 538 296 enum efx_tc_action_order new) 539 297 { 540 298 switch (new) { 299 + case EFX_TC_AO_DECAP: 300 + if (act->decap) 301 + return false; 302 + fallthrough; 541 303 case EFX_TC_AO_VLAN_POP: 542 304 if (act->vlan_pop >= 2) 543 305 return false; ··· 569 323 } 570 324 } 571 325 326 + static int efx_tc_flower_replace_foreign(struct efx_nic *efx, 327 + struct net_device *net_dev, 328 + struct flow_cls_offload *tc) 329 + { 330 + struct flow_rule *fr = flow_cls_offload_flow_rule(tc); 331 + struct netlink_ext_ack *extack = tc->common.extack; 332 + struct efx_tc_flow_rule *rule = NULL, *old = NULL; 333 + struct efx_tc_action_set *act = NULL; 334 + bool found = false, uplinked = false; 335 + const struct flow_action_entry *fa; 336 + struct efx_tc_match match; 337 + struct efx_rep *to_efv; 338 + s64 rc; 339 + int i; 340 + 341 + /* Parse match */ 342 + memset(&match, 0, sizeof(match)); 343 + rc = efx_tc_flower_parse_match(efx, fr, &match, NULL); 344 + if (rc) 345 + return rc; 346 + /* The rule as given to us doesn't specify a source netdevice. 347 + * But, determining whether packets from a VF should match it is 348 + * complicated, so leave those to the software slowpath: qualify 349 + * the filter with source m-port == wire. 350 + */ 351 + rc = efx_tc_flower_external_mport(efx, EFX_EFV_PF); 352 + if (rc < 0) { 353 + NL_SET_ERR_MSG_MOD(extack, "Failed to identify ingress m-port for foreign filter"); 354 + return rc; 355 + } 356 + match.value.ingress_port = rc; 357 + match.mask.ingress_port = ~0; 358 + 359 + if (tc->common.chain_index) { 360 + NL_SET_ERR_MSG_MOD(extack, "No support for nonzero chain_index"); 361 + return -EOPNOTSUPP; 362 + } 363 + match.mask.recirc_id = 0xff; 364 + 365 + flow_action_for_each(i, fa, &fr->action) { 366 + switch (fa->id) { 367 + case FLOW_ACTION_REDIRECT: 368 + case FLOW_ACTION_MIRRED: /* mirred means mirror here */ 369 + to_efv = efx_tc_flower_lookup_efv(efx, fa->dev); 370 + if (IS_ERR(to_efv)) 371 + continue; 372 + found = true; 373 + break; 374 + default: 375 + break; 376 + } 377 + } 378 + if (!found) { /* We don't care. */ 379 + netif_dbg(efx, drv, efx->net_dev, 380 + "Ignoring foreign filter that doesn't egdev us\n"); 381 + rc = -EOPNOTSUPP; 382 + goto release; 383 + } 384 + 385 + rc = efx_mae_match_check_caps(efx, &match.mask, NULL); 386 + if (rc) 387 + goto release; 388 + 389 + if (efx_tc_match_is_encap(&match.mask)) { 390 + enum efx_encap_type type; 391 + 392 + type = efx_tc_indr_netdev_type(net_dev); 393 + if (type == EFX_ENCAP_TYPE_NONE) { 394 + NL_SET_ERR_MSG_MOD(extack, 395 + "Egress encap match on unsupported tunnel device"); 396 + rc = -EOPNOTSUPP; 397 + goto release; 398 + } 399 + 400 + rc = efx_mae_check_encap_type_supported(efx, type); 401 + if (rc) { 402 + NL_SET_ERR_MSG_FMT_MOD(extack, 403 + "Firmware reports no support for %s encap match", 404 + efx_tc_encap_type_name(type)); 405 + goto release; 406 + } 407 + 408 + rc = efx_tc_flower_record_encap_match(efx, &match, type, 409 + extack); 410 + if (rc) 411 + goto release; 412 + } else { 413 + /* This is not a tunnel decap rule, ignore it */ 414 + netif_dbg(efx, drv, efx->net_dev, 415 + "Ignoring foreign filter without encap match\n"); 416 + rc = -EOPNOTSUPP; 417 + goto release; 418 + } 419 + 420 + rule = kzalloc(sizeof(*rule), GFP_USER); 421 + if (!rule) { 422 + rc = -ENOMEM; 423 + goto release; 424 + } 425 + INIT_LIST_HEAD(&rule->acts.list); 426 + rule->cookie = tc->cookie; 427 + old = rhashtable_lookup_get_insert_fast(&efx->tc->match_action_ht, 428 + &rule->linkage, 429 + efx_tc_match_action_ht_params); 430 + if (old) { 431 + netif_dbg(efx, drv, efx->net_dev, 432 + "Ignoring already-offloaded rule (cookie %lx)\n", 433 + tc->cookie); 434 + rc = -EEXIST; 435 + goto release; 436 + } 437 + 438 + act = kzalloc(sizeof(*act), GFP_USER); 439 + if (!act) { 440 + rc = -ENOMEM; 441 + goto release; 442 + } 443 + 444 + /* Parse actions. For foreign rules we only support decap & redirect. 445 + * See corresponding code in efx_tc_flower_replace() for theory of 446 + * operation & how 'act' cursor is used. 447 + */ 448 + flow_action_for_each(i, fa, &fr->action) { 449 + struct efx_tc_action_set save; 450 + 451 + switch (fa->id) { 452 + case FLOW_ACTION_REDIRECT: 453 + case FLOW_ACTION_MIRRED: 454 + /* See corresponding code in efx_tc_flower_replace() for 455 + * long explanations of what's going on here. 456 + */ 457 + save = *act; 458 + if (fa->hw_stats) { 459 + struct efx_tc_counter_index *ctr; 460 + 461 + if (!(fa->hw_stats & FLOW_ACTION_HW_STATS_DELAYED)) { 462 + NL_SET_ERR_MSG_FMT_MOD(extack, 463 + "hw_stats_type %u not supported (only 'delayed')", 464 + fa->hw_stats); 465 + rc = -EOPNOTSUPP; 466 + goto release; 467 + } 468 + if (!efx_tc_flower_action_order_ok(act, EFX_TC_AO_COUNT)) { 469 + rc = -EOPNOTSUPP; 470 + goto release; 471 + } 472 + 473 + ctr = efx_tc_flower_get_counter_index(efx, 474 + tc->cookie, 475 + EFX_TC_COUNTER_TYPE_AR); 476 + if (IS_ERR(ctr)) { 477 + rc = PTR_ERR(ctr); 478 + NL_SET_ERR_MSG_MOD(extack, "Failed to obtain a counter"); 479 + goto release; 480 + } 481 + act->count = ctr; 482 + } 483 + 484 + if (!efx_tc_flower_action_order_ok(act, EFX_TC_AO_DELIVER)) { 485 + /* can't happen */ 486 + rc = -EOPNOTSUPP; 487 + NL_SET_ERR_MSG_MOD(extack, 488 + "Deliver action violates action order (can't happen)"); 489 + goto release; 490 + } 491 + to_efv = efx_tc_flower_lookup_efv(efx, fa->dev); 492 + /* PF implies egdev is us, in which case we really 493 + * want to deliver to the uplink (because this is an 494 + * ingress filter). If we don't recognise the egdev 495 + * at all, then we'd better trap so SW can handle it. 496 + */ 497 + if (IS_ERR(to_efv)) 498 + to_efv = EFX_EFV_PF; 499 + if (to_efv == EFX_EFV_PF) { 500 + if (uplinked) 501 + break; 502 + uplinked = true; 503 + } 504 + rc = efx_tc_flower_internal_mport(efx, to_efv); 505 + if (rc < 0) { 506 + NL_SET_ERR_MSG_MOD(extack, "Failed to identify egress m-port"); 507 + goto release; 508 + } 509 + act->dest_mport = rc; 510 + act->deliver = 1; 511 + rc = efx_mae_alloc_action_set(efx, act); 512 + if (rc) { 513 + NL_SET_ERR_MSG_MOD(extack, 514 + "Failed to write action set to hw (mirred)"); 515 + goto release; 516 + } 517 + list_add_tail(&act->list, &rule->acts.list); 518 + act = NULL; 519 + if (fa->id == FLOW_ACTION_REDIRECT) 520 + break; /* end of the line */ 521 + /* Mirror, so continue on with saved act */ 522 + act = kzalloc(sizeof(*act), GFP_USER); 523 + if (!act) { 524 + rc = -ENOMEM; 525 + goto release; 526 + } 527 + *act = save; 528 + break; 529 + case FLOW_ACTION_TUNNEL_DECAP: 530 + if (!efx_tc_flower_action_order_ok(act, EFX_TC_AO_DECAP)) { 531 + rc = -EINVAL; 532 + NL_SET_ERR_MSG_MOD(extack, "Decap action violates action order"); 533 + goto release; 534 + } 535 + act->decap = 1; 536 + /* If we previously delivered/trapped to uplink, now 537 + * that we've decapped we'll want another copy if we 538 + * try to deliver/trap to uplink again. 539 + */ 540 + uplinked = false; 541 + break; 542 + default: 543 + NL_SET_ERR_MSG_FMT_MOD(extack, "Unhandled action %u", 544 + fa->id); 545 + rc = -EOPNOTSUPP; 546 + goto release; 547 + } 548 + } 549 + 550 + if (act) { 551 + if (!uplinked) { 552 + /* Not shot/redirected, so deliver to default dest (which is 553 + * the uplink, as this is an ingress filter) 554 + */ 555 + efx_mae_mport_uplink(efx, &act->dest_mport); 556 + act->deliver = 1; 557 + } 558 + rc = efx_mae_alloc_action_set(efx, act); 559 + if (rc) { 560 + NL_SET_ERR_MSG_MOD(extack, "Failed to write action set to hw (deliver)"); 561 + goto release; 562 + } 563 + list_add_tail(&act->list, &rule->acts.list); 564 + act = NULL; /* Prevent double-free in error path */ 565 + } 566 + 567 + rule->match = match; 568 + 569 + netif_dbg(efx, drv, efx->net_dev, 570 + "Successfully parsed foreign filter (cookie %lx)\n", 571 + tc->cookie); 572 + 573 + rc = efx_mae_alloc_action_set_list(efx, &rule->acts); 574 + if (rc) { 575 + NL_SET_ERR_MSG_MOD(extack, "Failed to write action set list to hw"); 576 + goto release; 577 + } 578 + rc = efx_mae_insert_rule(efx, &rule->match, EFX_TC_PRIO_TC, 579 + rule->acts.fw_id, &rule->fw_id); 580 + if (rc) { 581 + NL_SET_ERR_MSG_MOD(extack, "Failed to insert rule in hw"); 582 + goto release_acts; 583 + } 584 + return 0; 585 + 586 + release_acts: 587 + efx_mae_free_action_set_list(efx, &rule->acts); 588 + release: 589 + /* We failed to insert the rule, so free up any entries we created in 590 + * subsidiary tables. 591 + */ 592 + if (act) 593 + efx_tc_free_action_set(efx, act, false); 594 + if (rule) { 595 + rhashtable_remove_fast(&efx->tc->match_action_ht, 596 + &rule->linkage, 597 + efx_tc_match_action_ht_params); 598 + efx_tc_free_action_set_list(efx, &rule->acts, false); 599 + } 600 + kfree(rule); 601 + if (match.encap) 602 + efx_tc_flower_release_encap_match(efx, match.encap); 603 + return rc; 604 + } 605 + 572 606 static int efx_tc_flower_replace(struct efx_nic *efx, 573 607 struct net_device *net_dev, 574 608 struct flow_cls_offload *tc, ··· 873 347 874 348 from_efv = efx_tc_flower_lookup_efv(efx, net_dev); 875 349 if (IS_ERR(from_efv)) { 876 - /* Might be a tunnel decap rule from an indirect block. 877 - * Support for those not implemented yet. 878 - */ 879 - return -EOPNOTSUPP; 350 + /* Not from our PF or representors, so probably a tunnel dev */ 351 + return efx_tc_flower_replace_foreign(efx, net_dev, tc); 880 352 } 881 353 882 354 if (efv != from_efv) { ··· 897 373 rc = efx_tc_flower_parse_match(efx, fr, &match, extack); 898 374 if (rc) 899 375 return rc; 376 + if (efx_tc_match_is_encap(&match.mask)) { 377 + NL_SET_ERR_MSG_MOD(extack, "Ingress enc_key matches not supported"); 378 + rc = -EOPNOTSUPP; 379 + goto release; 380 + } 900 381 901 382 if (tc->common.chain_index) { 902 383 NL_SET_ERR_MSG_MOD(extack, "No support for nonzero chain_index"); ··· 936 407 goto release; 937 408 } 938 409 410 + /** 411 + * DOC: TC action translation 412 + * 413 + * Actions in TC are sequential and cumulative, with delivery actions 414 + * potentially anywhere in the order. The EF100 MAE, however, takes 415 + * an 'action set list' consisting of 'action sets', each of which is 416 + * applied to the _original_ packet, and consists of a set of optional 417 + * actions in a fixed order with delivery at the end. 418 + * To translate between these two models, we maintain a 'cursor', @act, 419 + * which describes the cumulative effect of all the packet-mutating 420 + * actions encountered so far; on handling a delivery (mirred or drop) 421 + * action, once the action-set has been inserted into hardware, we 422 + * append @act to the action-set list (@rule->acts); if this is a pipe 423 + * action (mirred mirror) we then allocate a new @act with a copy of 424 + * the cursor state _before_ the delivery action, otherwise we set @act 425 + * to %NULL. 426 + * This ensures that every allocated action-set is either attached to 427 + * @rule->acts or pointed to by @act (and never both), and that only 428 + * those action-sets in @rule->acts exist in hardware. Consequently, 429 + * in the failure path, @act only needs to be freed in memory, whereas 430 + * for @rule->acts we remove each action-set from hardware before 431 + * freeing it (efx_tc_free_action_set_list()), even if the action-set 432 + * list itself is not in hardware. 433 + */ 939 434 flow_action_for_each(i, fa, &fr->action) { 940 435 struct efx_tc_action_set save; 941 436 u16 tci; ··· 1442 889 efx->tc->up = false; 1443 890 } 1444 891 892 + /* At teardown time, all TC filter rules (and thus all resources they created) 893 + * should already have been removed. If we find any in our hashtables, make a 894 + * cursory attempt to clean up the software side. 895 + */ 896 + static void efx_tc_encap_match_free(void *ptr, void *__unused) 897 + { 898 + struct efx_tc_encap_match *encap = ptr; 899 + 900 + WARN_ON(refcount_read(&encap->ref)); 901 + kfree(encap); 902 + } 903 + 1445 904 int efx_init_struct_tc(struct efx_nic *efx) 1446 905 { 1447 906 int rc; ··· 1476 911 rc = efx_tc_init_counters(efx); 1477 912 if (rc < 0) 1478 913 goto fail_counters; 914 + rc = rhashtable_init(&efx->tc->encap_match_ht, &efx_tc_encap_match_ht_params); 915 + if (rc < 0) 916 + goto fail_encap_match_ht; 1479 917 rc = rhashtable_init(&efx->tc->match_action_ht, &efx_tc_match_action_ht_params); 1480 918 if (rc < 0) 1481 919 goto fail_match_action_ht; ··· 1491 923 efx->extra_channel_type[EFX_EXTRA_CHANNEL_TC] = &efx_tc_channel_type; 1492 924 return 0; 1493 925 fail_match_action_ht: 926 + rhashtable_destroy(&efx->tc->encap_match_ht); 927 + fail_encap_match_ht: 1494 928 efx_tc_destroy_counters(efx); 1495 929 fail_counters: 1496 930 mutex_destroy(&efx->tc->mutex); ··· 1515 945 MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL); 1516 946 rhashtable_free_and_destroy(&efx->tc->match_action_ht, efx_tc_flow_free, 1517 947 efx); 948 + rhashtable_free_and_destroy(&efx->tc->encap_match_ht, 949 + efx_tc_encap_match_free, NULL); 1518 950 efx_tc_fini_counters(efx); 1519 951 mutex_unlock(&efx->tc->mutex); 1520 952 mutex_destroy(&efx->tc->mutex);
+37
drivers/net/ethernet/sfc/tc.h
··· 18 18 19 19 #define IS_ALL_ONES(v) (!(typeof (v))~(v)) 20 20 21 + #ifdef CONFIG_IPV6 22 + static inline bool efx_ipv6_addr_all_ones(struct in6_addr *addr) 23 + { 24 + return !memchr_inv(addr, 0xff, sizeof(*addr)); 25 + } 26 + #endif 27 + 21 28 struct efx_tc_action_set { 22 29 u16 vlan_push:2; 23 30 u16 vlan_pop:2; 31 + u16 decap:1; 24 32 u16 deliver:1; 25 33 __be16 vlan_tci[2]; /* TCIs for vlan_push */ 26 34 __be16 vlan_proto[2]; /* Ethertypes for vlan_push */ ··· 56 48 /* L4 */ 57 49 __be16 l4_sport, l4_dport; /* Ports (UDP, TCP) */ 58 50 __be16 tcp_flags; 51 + /* Encap. The following are *outer* fields. Note that there are no 52 + * outer eth (L2) fields; this is because TC doesn't have them. 53 + */ 54 + __be32 enc_src_ip, enc_dst_ip; 55 + struct in6_addr enc_src_ip6, enc_dst_ip6; 56 + u8 enc_ip_tos, enc_ip_ttl; 57 + __be16 enc_sport, enc_dport; 58 + __be32 enc_keyid; /* e.g. VNI, VSID */ 59 + }; 60 + 61 + static inline bool efx_tc_match_is_encap(const struct efx_tc_match_fields *mask) 62 + { 63 + return mask->enc_src_ip || mask->enc_dst_ip || 64 + !ipv6_addr_any(&mask->enc_src_ip6) || 65 + !ipv6_addr_any(&mask->enc_dst_ip6) || mask->enc_ip_tos || 66 + mask->enc_ip_ttl || mask->enc_sport || mask->enc_dport; 67 + } 68 + 69 + struct efx_tc_encap_match { 70 + __be32 src_ip, dst_ip; 71 + struct in6_addr src_ip6, dst_ip6; 72 + __be16 udp_dport; 73 + struct rhash_head linkage; 74 + enum efx_encap_type tun_type; 75 + refcount_t ref; 76 + u32 fw_id; /* index of this entry in firmware encap match table */ 59 77 }; 60 78 61 79 struct efx_tc_match { 62 80 struct efx_tc_match_fields value; 63 81 struct efx_tc_match_fields mask; 82 + struct efx_tc_encap_match *encap; 64 83 }; 65 84 66 85 struct efx_tc_action_set_list { ··· 117 82 * @mutex: Used to serialise operations on TC hashtables 118 83 * @counter_ht: Hashtable of TC counters (FW IDs and counter values) 119 84 * @counter_id_ht: Hashtable mapping TC counter cookies to counters 85 + * @encap_match_ht: Hashtable of TC encap matches 120 86 * @match_action_ht: Hashtable of TC match-action rules 121 87 * @reps_mport_id: MAE port allocated for representor RX 122 88 * @reps_filter_uc: VNIC filter for representor unicast RX (promisc) ··· 141 105 struct mutex mutex; 142 106 struct rhashtable counter_ht; 143 107 struct rhashtable counter_id_ht; 108 + struct rhashtable encap_match_ht; 144 109 struct rhashtable match_action_ht; 145 110 u32 reps_mport_id, reps_mport_vport_id; 146 111 s32 reps_filter_uc, reps_filter_mc;