162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 262306a36Sopenharmony_ci/* Copyright (C) 2021 Corigine, Inc. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <net/tc_act/tc_csum.h> 562306a36Sopenharmony_ci#include <net/tc_act/tc_ct.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "conntrack.h" 862306a36Sopenharmony_ci#include "../nfp_port.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ciconst struct rhashtable_params nfp_tc_ct_merge_params = { 1162306a36Sopenharmony_ci .head_offset = offsetof(struct nfp_fl_ct_tc_merge, 1262306a36Sopenharmony_ci hash_node), 1362306a36Sopenharmony_ci .key_len = sizeof(unsigned long) * 2, 1462306a36Sopenharmony_ci .key_offset = offsetof(struct nfp_fl_ct_tc_merge, cookie), 1562306a36Sopenharmony_ci .automatic_shrinking = true, 1662306a36Sopenharmony_ci}; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ciconst struct rhashtable_params nfp_nft_ct_merge_params = { 1962306a36Sopenharmony_ci .head_offset = offsetof(struct nfp_fl_nft_tc_merge, 2062306a36Sopenharmony_ci hash_node), 2162306a36Sopenharmony_ci .key_len = sizeof(unsigned long) * 3, 2262306a36Sopenharmony_ci .key_offset = offsetof(struct nfp_fl_nft_tc_merge, cookie), 2362306a36Sopenharmony_ci .automatic_shrinking = true, 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic struct flow_action_entry *get_flow_act(struct flow_rule *rule, 2762306a36Sopenharmony_ci enum flow_action_id act_id); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/** 3062306a36Sopenharmony_ci * get_hashentry() - Wrapper around hashtable lookup. 3162306a36Sopenharmony_ci * @ht: hashtable where entry could be found 3262306a36Sopenharmony_ci * @key: key to lookup 3362306a36Sopenharmony_ci * @params: hashtable params 3462306a36Sopenharmony_ci * @size: size of entry to allocate if not in table 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * Returns an entry from a hashtable. If entry does not exist 3762306a36Sopenharmony_ci * yet allocate the memory for it and return the new entry. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_cistatic void *get_hashentry(struct rhashtable *ht, void *key, 4062306a36Sopenharmony_ci const struct rhashtable_params params, size_t size) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci void *result; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci result = rhashtable_lookup_fast(ht, key, params); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (result) 4762306a36Sopenharmony_ci return result; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci result = kzalloc(size, GFP_KERNEL); 5062306a36Sopenharmony_ci if (!result) 5162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return result; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cibool is_pre_ct_flow(struct flow_cls_offload *flow) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct flow_rule *rule = flow_cls_offload_flow_rule(flow); 5962306a36Sopenharmony_ci struct flow_dissector *dissector = rule->match.dissector; 6062306a36Sopenharmony_ci struct flow_action_entry *act; 6162306a36Sopenharmony_ci struct flow_match_ct ct; 6262306a36Sopenharmony_ci int i; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (dissector->used_keys & BIT_ULL(FLOW_DISSECTOR_KEY_CT)) { 6562306a36Sopenharmony_ci flow_rule_match_ct(rule, &ct); 6662306a36Sopenharmony_ci if (ct.key->ct_state) 6762306a36Sopenharmony_ci return false; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (flow->common.chain_index) 7162306a36Sopenharmony_ci return false; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci flow_action_for_each(i, act, &flow->rule->action) { 7462306a36Sopenharmony_ci if (act->id == FLOW_ACTION_CT) { 7562306a36Sopenharmony_ci /* The pre_ct rule only have the ct or ct nat action, cannot 7662306a36Sopenharmony_ci * contains other ct action e.g ct commit and so on. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci if ((!act->ct.action || act->ct.action == TCA_CT_ACT_NAT)) 7962306a36Sopenharmony_ci return true; 8062306a36Sopenharmony_ci else 8162306a36Sopenharmony_ci return false; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return false; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cibool is_post_ct_flow(struct flow_cls_offload *flow) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct flow_rule *rule = flow_cls_offload_flow_rule(flow); 9162306a36Sopenharmony_ci struct flow_dissector *dissector = rule->match.dissector; 9262306a36Sopenharmony_ci struct flow_action_entry *act; 9362306a36Sopenharmony_ci bool exist_ct_clear = false; 9462306a36Sopenharmony_ci struct flow_match_ct ct; 9562306a36Sopenharmony_ci int i; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (dissector->used_keys & BIT_ULL(FLOW_DISSECTOR_KEY_CT)) { 9862306a36Sopenharmony_ci flow_rule_match_ct(rule, &ct); 9962306a36Sopenharmony_ci if (ct.key->ct_state & TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED) 10062306a36Sopenharmony_ci return true; 10162306a36Sopenharmony_ci } else { 10262306a36Sopenharmony_ci /* post ct entry cannot contains any ct action except ct_clear. */ 10362306a36Sopenharmony_ci flow_action_for_each(i, act, &flow->rule->action) { 10462306a36Sopenharmony_ci if (act->id == FLOW_ACTION_CT) { 10562306a36Sopenharmony_ci /* ignore ct clear action. */ 10662306a36Sopenharmony_ci if (act->ct.action == TCA_CT_ACT_CLEAR) { 10762306a36Sopenharmony_ci exist_ct_clear = true; 10862306a36Sopenharmony_ci continue; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return false; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci /* when do nat with ct, the post ct entry ignore the ct status, 11562306a36Sopenharmony_ci * will match the nat field(sip/dip) instead. In this situation, 11662306a36Sopenharmony_ci * the flow chain index is not zero and contains ct clear action. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci if (flow->common.chain_index && exist_ct_clear) 11962306a36Sopenharmony_ci return true; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return false; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/** 12662306a36Sopenharmony_ci * get_mangled_key() - Mangle the key if mangle act exists 12762306a36Sopenharmony_ci * @rule: rule that carries the actions 12862306a36Sopenharmony_ci * @buf: pointer to key to be mangled 12962306a36Sopenharmony_ci * @offset: used to adjust mangled offset in L2/L3/L4 header 13062306a36Sopenharmony_ci * @key_sz: key size 13162306a36Sopenharmony_ci * @htype: mangling type 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * Returns buf where the mangled key stores. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_cistatic void *get_mangled_key(struct flow_rule *rule, void *buf, 13662306a36Sopenharmony_ci u32 offset, size_t key_sz, 13762306a36Sopenharmony_ci enum flow_action_mangle_base htype) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct flow_action_entry *act; 14062306a36Sopenharmony_ci u32 *val = (u32 *)buf; 14162306a36Sopenharmony_ci u32 off, msk, key; 14262306a36Sopenharmony_ci int i; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci flow_action_for_each(i, act, &rule->action) { 14562306a36Sopenharmony_ci if (act->id == FLOW_ACTION_MANGLE && 14662306a36Sopenharmony_ci act->mangle.htype == htype) { 14762306a36Sopenharmony_ci off = act->mangle.offset - offset; 14862306a36Sopenharmony_ci msk = act->mangle.mask; 14962306a36Sopenharmony_ci key = act->mangle.val; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Mangling is supposed to be u32 aligned */ 15262306a36Sopenharmony_ci if (off % 4 || off >= key_sz) 15362306a36Sopenharmony_ci continue; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci val[off >> 2] &= msk; 15662306a36Sopenharmony_ci val[off >> 2] |= key; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return buf; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* Only tos and ttl are involved in flow_match_ip structure, which 16462306a36Sopenharmony_ci * doesn't conform to the layout of ip/ipv6 header definition. So 16562306a36Sopenharmony_ci * they need particular process here: fill them into the ip/ipv6 16662306a36Sopenharmony_ci * header, so that mangling actions can work directly. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci#define NFP_IPV4_TOS_MASK GENMASK(23, 16) 16962306a36Sopenharmony_ci#define NFP_IPV4_TTL_MASK GENMASK(31, 24) 17062306a36Sopenharmony_ci#define NFP_IPV6_TCLASS_MASK GENMASK(27, 20) 17162306a36Sopenharmony_ci#define NFP_IPV6_HLIMIT_MASK GENMASK(7, 0) 17262306a36Sopenharmony_cistatic void *get_mangled_tos_ttl(struct flow_rule *rule, void *buf, 17362306a36Sopenharmony_ci bool is_v6) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct flow_match_ip match; 17662306a36Sopenharmony_ci /* IPv4's ttl field is in third dword. */ 17762306a36Sopenharmony_ci __be32 ip_hdr[3]; 17862306a36Sopenharmony_ci u32 tmp, hdr_len; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci flow_rule_match_ip(rule, &match); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (is_v6) { 18362306a36Sopenharmony_ci tmp = FIELD_PREP(NFP_IPV6_TCLASS_MASK, match.key->tos); 18462306a36Sopenharmony_ci ip_hdr[0] = cpu_to_be32(tmp); 18562306a36Sopenharmony_ci tmp = FIELD_PREP(NFP_IPV6_HLIMIT_MASK, match.key->ttl); 18662306a36Sopenharmony_ci ip_hdr[1] = cpu_to_be32(tmp); 18762306a36Sopenharmony_ci hdr_len = 2 * sizeof(__be32); 18862306a36Sopenharmony_ci } else { 18962306a36Sopenharmony_ci tmp = FIELD_PREP(NFP_IPV4_TOS_MASK, match.key->tos); 19062306a36Sopenharmony_ci ip_hdr[0] = cpu_to_be32(tmp); 19162306a36Sopenharmony_ci tmp = FIELD_PREP(NFP_IPV4_TTL_MASK, match.key->ttl); 19262306a36Sopenharmony_ci ip_hdr[2] = cpu_to_be32(tmp); 19362306a36Sopenharmony_ci hdr_len = 3 * sizeof(__be32); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci get_mangled_key(rule, ip_hdr, 0, hdr_len, 19762306a36Sopenharmony_ci is_v6 ? FLOW_ACT_MANGLE_HDR_TYPE_IP6 : 19862306a36Sopenharmony_ci FLOW_ACT_MANGLE_HDR_TYPE_IP4); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci match.key = buf; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (is_v6) { 20362306a36Sopenharmony_ci tmp = be32_to_cpu(ip_hdr[0]); 20462306a36Sopenharmony_ci match.key->tos = FIELD_GET(NFP_IPV6_TCLASS_MASK, tmp); 20562306a36Sopenharmony_ci tmp = be32_to_cpu(ip_hdr[1]); 20662306a36Sopenharmony_ci match.key->ttl = FIELD_GET(NFP_IPV6_HLIMIT_MASK, tmp); 20762306a36Sopenharmony_ci } else { 20862306a36Sopenharmony_ci tmp = be32_to_cpu(ip_hdr[0]); 20962306a36Sopenharmony_ci match.key->tos = FIELD_GET(NFP_IPV4_TOS_MASK, tmp); 21062306a36Sopenharmony_ci tmp = be32_to_cpu(ip_hdr[2]); 21162306a36Sopenharmony_ci match.key->ttl = FIELD_GET(NFP_IPV4_TTL_MASK, tmp); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return buf; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/* Note entry1 and entry2 are not swappable. only skip ip and 21862306a36Sopenharmony_ci * tport merge check for pre_ct and post_ct when pre_ct do nat. 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_cistatic bool nfp_ct_merge_check_cannot_skip(struct nfp_fl_ct_flow_entry *entry1, 22162306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *entry2) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci /* only pre_ct have NFP_FL_ACTION_DO_NAT flag. */ 22462306a36Sopenharmony_ci if ((entry1->flags & NFP_FL_ACTION_DO_NAT) && 22562306a36Sopenharmony_ci entry2->type == CT_TYPE_POST_CT) 22662306a36Sopenharmony_ci return false; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return true; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/* Note entry1 and entry2 are not swappable, entry1 should be 23262306a36Sopenharmony_ci * the former flow whose mangle action need be taken into account 23362306a36Sopenharmony_ci * if existed, and entry2 should be the latter flow whose action 23462306a36Sopenharmony_ci * we don't care. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_cistatic int nfp_ct_merge_check(struct nfp_fl_ct_flow_entry *entry1, 23762306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *entry2) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci unsigned long long ovlp_keys; 24062306a36Sopenharmony_ci bool out, is_v6 = false; 24162306a36Sopenharmony_ci u8 ip_proto = 0; 24262306a36Sopenharmony_ci ovlp_keys = entry1->rule->match.dissector->used_keys & 24362306a36Sopenharmony_ci entry2->rule->match.dissector->used_keys; 24462306a36Sopenharmony_ci /* Temporary buffer for mangling keys, 64 is enough to cover max 24562306a36Sopenharmony_ci * struct size of key in various fields that may be mangled. 24662306a36Sopenharmony_ci * Supported fields to mangle: 24762306a36Sopenharmony_ci * mac_src/mac_dst(struct flow_match_eth_addrs, 12B) 24862306a36Sopenharmony_ci * nw_tos/nw_ttl(struct flow_match_ip, 2B) 24962306a36Sopenharmony_ci * nw_src/nw_dst(struct flow_match_ipv4/6_addrs, 32B) 25062306a36Sopenharmony_ci * tp_src/tp_dst(struct flow_match_ports, 4B) 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ci char buf[64]; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (entry1->netdev && entry2->netdev && 25562306a36Sopenharmony_ci entry1->netdev != entry2->netdev) 25662306a36Sopenharmony_ci return -EINVAL; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Check the overlapped fields one by one, the unmasked part 25962306a36Sopenharmony_ci * should not conflict with each other. 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ci if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL)) { 26262306a36Sopenharmony_ci struct flow_match_control match1, match2; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci flow_rule_match_control(entry1->rule, &match1); 26562306a36Sopenharmony_ci flow_rule_match_control(entry2->rule, &match2); 26662306a36Sopenharmony_ci COMPARE_UNMASKED_FIELDS(match1, match2, &out); 26762306a36Sopenharmony_ci if (out) 26862306a36Sopenharmony_ci goto check_failed; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_BASIC)) { 27262306a36Sopenharmony_ci struct flow_match_basic match1, match2; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci flow_rule_match_basic(entry1->rule, &match1); 27562306a36Sopenharmony_ci flow_rule_match_basic(entry2->rule, &match2); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* n_proto field is a must in ct-related flows, 27862306a36Sopenharmony_ci * it should be either ipv4 or ipv6. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci is_v6 = match1.key->n_proto == htons(ETH_P_IPV6); 28162306a36Sopenharmony_ci /* ip_proto field is a must when port field is cared */ 28262306a36Sopenharmony_ci ip_proto = match1.key->ip_proto; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci COMPARE_UNMASKED_FIELDS(match1, match2, &out); 28562306a36Sopenharmony_ci if (out) 28662306a36Sopenharmony_ci goto check_failed; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* if pre ct entry do nat, the nat ip exists in nft entry, 29062306a36Sopenharmony_ci * will be do merge check when do nft and post ct merge, 29162306a36Sopenharmony_ci * so skip this ip merge check here. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ci if ((ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS)) && 29462306a36Sopenharmony_ci nfp_ct_merge_check_cannot_skip(entry1, entry2)) { 29562306a36Sopenharmony_ci struct flow_match_ipv4_addrs match1, match2; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci flow_rule_match_ipv4_addrs(entry1->rule, &match1); 29862306a36Sopenharmony_ci flow_rule_match_ipv4_addrs(entry2->rule, &match2); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci memcpy(buf, match1.key, sizeof(*match1.key)); 30162306a36Sopenharmony_ci match1.key = get_mangled_key(entry1->rule, buf, 30262306a36Sopenharmony_ci offsetof(struct iphdr, saddr), 30362306a36Sopenharmony_ci sizeof(*match1.key), 30462306a36Sopenharmony_ci FLOW_ACT_MANGLE_HDR_TYPE_IP4); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci COMPARE_UNMASKED_FIELDS(match1, match2, &out); 30762306a36Sopenharmony_ci if (out) 30862306a36Sopenharmony_ci goto check_failed; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* if pre ct entry do nat, the nat ip exists in nft entry, 31262306a36Sopenharmony_ci * will be do merge check when do nft and post ct merge, 31362306a36Sopenharmony_ci * so skip this ip merge check here. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci if ((ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS)) && 31662306a36Sopenharmony_ci nfp_ct_merge_check_cannot_skip(entry1, entry2)) { 31762306a36Sopenharmony_ci struct flow_match_ipv6_addrs match1, match2; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci flow_rule_match_ipv6_addrs(entry1->rule, &match1); 32062306a36Sopenharmony_ci flow_rule_match_ipv6_addrs(entry2->rule, &match2); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci memcpy(buf, match1.key, sizeof(*match1.key)); 32362306a36Sopenharmony_ci match1.key = get_mangled_key(entry1->rule, buf, 32462306a36Sopenharmony_ci offsetof(struct ipv6hdr, saddr), 32562306a36Sopenharmony_ci sizeof(*match1.key), 32662306a36Sopenharmony_ci FLOW_ACT_MANGLE_HDR_TYPE_IP6); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci COMPARE_UNMASKED_FIELDS(match1, match2, &out); 32962306a36Sopenharmony_ci if (out) 33062306a36Sopenharmony_ci goto check_failed; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* if pre ct entry do nat, the nat tport exists in nft entry, 33462306a36Sopenharmony_ci * will be do merge check when do nft and post ct merge, 33562306a36Sopenharmony_ci * so skip this tport merge check here. 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ci if ((ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_PORTS)) && 33862306a36Sopenharmony_ci nfp_ct_merge_check_cannot_skip(entry1, entry2)) { 33962306a36Sopenharmony_ci enum flow_action_mangle_base htype = FLOW_ACT_MANGLE_UNSPEC; 34062306a36Sopenharmony_ci struct flow_match_ports match1, match2; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci flow_rule_match_ports(entry1->rule, &match1); 34362306a36Sopenharmony_ci flow_rule_match_ports(entry2->rule, &match2); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (ip_proto == IPPROTO_UDP) 34662306a36Sopenharmony_ci htype = FLOW_ACT_MANGLE_HDR_TYPE_UDP; 34762306a36Sopenharmony_ci else if (ip_proto == IPPROTO_TCP) 34862306a36Sopenharmony_ci htype = FLOW_ACT_MANGLE_HDR_TYPE_TCP; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci memcpy(buf, match1.key, sizeof(*match1.key)); 35162306a36Sopenharmony_ci match1.key = get_mangled_key(entry1->rule, buf, 0, 35262306a36Sopenharmony_ci sizeof(*match1.key), htype); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci COMPARE_UNMASKED_FIELDS(match1, match2, &out); 35562306a36Sopenharmony_ci if (out) 35662306a36Sopenharmony_ci goto check_failed; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS)) { 36062306a36Sopenharmony_ci struct flow_match_eth_addrs match1, match2; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci flow_rule_match_eth_addrs(entry1->rule, &match1); 36362306a36Sopenharmony_ci flow_rule_match_eth_addrs(entry2->rule, &match2); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci memcpy(buf, match1.key, sizeof(*match1.key)); 36662306a36Sopenharmony_ci match1.key = get_mangled_key(entry1->rule, buf, 0, 36762306a36Sopenharmony_ci sizeof(*match1.key), 36862306a36Sopenharmony_ci FLOW_ACT_MANGLE_HDR_TYPE_ETH); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci COMPARE_UNMASKED_FIELDS(match1, match2, &out); 37162306a36Sopenharmony_ci if (out) 37262306a36Sopenharmony_ci goto check_failed; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_VLAN)) { 37662306a36Sopenharmony_ci struct flow_match_vlan match1, match2; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci flow_rule_match_vlan(entry1->rule, &match1); 37962306a36Sopenharmony_ci flow_rule_match_vlan(entry2->rule, &match2); 38062306a36Sopenharmony_ci COMPARE_UNMASKED_FIELDS(match1, match2, &out); 38162306a36Sopenharmony_ci if (out) 38262306a36Sopenharmony_ci goto check_failed; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_MPLS)) { 38662306a36Sopenharmony_ci struct flow_match_mpls match1, match2; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci flow_rule_match_mpls(entry1->rule, &match1); 38962306a36Sopenharmony_ci flow_rule_match_mpls(entry2->rule, &match2); 39062306a36Sopenharmony_ci COMPARE_UNMASKED_FIELDS(match1, match2, &out); 39162306a36Sopenharmony_ci if (out) 39262306a36Sopenharmony_ci goto check_failed; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_TCP)) { 39662306a36Sopenharmony_ci struct flow_match_tcp match1, match2; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci flow_rule_match_tcp(entry1->rule, &match1); 39962306a36Sopenharmony_ci flow_rule_match_tcp(entry2->rule, &match2); 40062306a36Sopenharmony_ci COMPARE_UNMASKED_FIELDS(match1, match2, &out); 40162306a36Sopenharmony_ci if (out) 40262306a36Sopenharmony_ci goto check_failed; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_IP)) { 40662306a36Sopenharmony_ci struct flow_match_ip match1, match2; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci flow_rule_match_ip(entry1->rule, &match1); 40962306a36Sopenharmony_ci flow_rule_match_ip(entry2->rule, &match2); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci match1.key = get_mangled_tos_ttl(entry1->rule, buf, is_v6); 41262306a36Sopenharmony_ci COMPARE_UNMASKED_FIELDS(match1, match2, &out); 41362306a36Sopenharmony_ci if (out) 41462306a36Sopenharmony_ci goto check_failed; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_ENC_KEYID)) { 41862306a36Sopenharmony_ci struct flow_match_enc_keyid match1, match2; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci flow_rule_match_enc_keyid(entry1->rule, &match1); 42162306a36Sopenharmony_ci flow_rule_match_enc_keyid(entry2->rule, &match2); 42262306a36Sopenharmony_ci COMPARE_UNMASKED_FIELDS(match1, match2, &out); 42362306a36Sopenharmony_ci if (out) 42462306a36Sopenharmony_ci goto check_failed; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) { 42862306a36Sopenharmony_ci struct flow_match_ipv4_addrs match1, match2; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci flow_rule_match_enc_ipv4_addrs(entry1->rule, &match1); 43162306a36Sopenharmony_ci flow_rule_match_enc_ipv4_addrs(entry2->rule, &match2); 43262306a36Sopenharmony_ci COMPARE_UNMASKED_FIELDS(match1, match2, &out); 43362306a36Sopenharmony_ci if (out) 43462306a36Sopenharmony_ci goto check_failed; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS)) { 43862306a36Sopenharmony_ci struct flow_match_ipv6_addrs match1, match2; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci flow_rule_match_enc_ipv6_addrs(entry1->rule, &match1); 44162306a36Sopenharmony_ci flow_rule_match_enc_ipv6_addrs(entry2->rule, &match2); 44262306a36Sopenharmony_ci COMPARE_UNMASKED_FIELDS(match1, match2, &out); 44362306a36Sopenharmony_ci if (out) 44462306a36Sopenharmony_ci goto check_failed; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_ENC_CONTROL)) { 44862306a36Sopenharmony_ci struct flow_match_control match1, match2; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci flow_rule_match_enc_control(entry1->rule, &match1); 45162306a36Sopenharmony_ci flow_rule_match_enc_control(entry2->rule, &match2); 45262306a36Sopenharmony_ci COMPARE_UNMASKED_FIELDS(match1, match2, &out); 45362306a36Sopenharmony_ci if (out) 45462306a36Sopenharmony_ci goto check_failed; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_ENC_IP)) { 45862306a36Sopenharmony_ci struct flow_match_ip match1, match2; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci flow_rule_match_enc_ip(entry1->rule, &match1); 46162306a36Sopenharmony_ci flow_rule_match_enc_ip(entry2->rule, &match2); 46262306a36Sopenharmony_ci COMPARE_UNMASKED_FIELDS(match1, match2, &out); 46362306a36Sopenharmony_ci if (out) 46462306a36Sopenharmony_ci goto check_failed; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_ENC_OPTS)) { 46862306a36Sopenharmony_ci struct flow_match_enc_opts match1, match2; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci flow_rule_match_enc_opts(entry1->rule, &match1); 47162306a36Sopenharmony_ci flow_rule_match_enc_opts(entry2->rule, &match2); 47262306a36Sopenharmony_ci COMPARE_UNMASKED_FIELDS(match1, match2, &out); 47362306a36Sopenharmony_ci if (out) 47462306a36Sopenharmony_ci goto check_failed; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return 0; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cicheck_failed: 48062306a36Sopenharmony_ci return -EINVAL; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic int nfp_ct_check_vlan_merge(struct flow_action_entry *a_in, 48462306a36Sopenharmony_ci struct flow_rule *rule) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct flow_match_vlan match; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (unlikely(flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CVLAN))) 48962306a36Sopenharmony_ci return -EOPNOTSUPP; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* post_ct does not match VLAN KEY, can be merged. */ 49262306a36Sopenharmony_ci if (likely(!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN))) 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci switch (a_in->id) { 49662306a36Sopenharmony_ci /* pre_ct has pop vlan, post_ct cannot match VLAN KEY, cannot be merged. */ 49762306a36Sopenharmony_ci case FLOW_ACTION_VLAN_POP: 49862306a36Sopenharmony_ci return -EOPNOTSUPP; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci case FLOW_ACTION_VLAN_PUSH: 50162306a36Sopenharmony_ci case FLOW_ACTION_VLAN_MANGLE: 50262306a36Sopenharmony_ci flow_rule_match_vlan(rule, &match); 50362306a36Sopenharmony_ci /* different vlan id, cannot be merged. */ 50462306a36Sopenharmony_ci if ((match.key->vlan_id & match.mask->vlan_id) ^ 50562306a36Sopenharmony_ci (a_in->vlan.vid & match.mask->vlan_id)) 50662306a36Sopenharmony_ci return -EOPNOTSUPP; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* different tpid, cannot be merged. */ 50962306a36Sopenharmony_ci if ((match.key->vlan_tpid & match.mask->vlan_tpid) ^ 51062306a36Sopenharmony_ci (a_in->vlan.proto & match.mask->vlan_tpid)) 51162306a36Sopenharmony_ci return -EOPNOTSUPP; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci /* different priority, cannot be merged. */ 51462306a36Sopenharmony_ci if ((match.key->vlan_priority & match.mask->vlan_priority) ^ 51562306a36Sopenharmony_ci (a_in->vlan.prio & match.mask->vlan_priority)) 51662306a36Sopenharmony_ci return -EOPNOTSUPP; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci break; 51962306a36Sopenharmony_ci default: 52062306a36Sopenharmony_ci return -EOPNOTSUPP; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci return 0; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci/* Extra check for multiple ct-zones merge 52762306a36Sopenharmony_ci * currently surpport nft entries merge check in different zones 52862306a36Sopenharmony_ci */ 52962306a36Sopenharmony_cistatic int nfp_ct_merge_extra_check(struct nfp_fl_ct_flow_entry *nft_entry, 53062306a36Sopenharmony_ci struct nfp_fl_ct_tc_merge *tc_m_entry) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci struct nfp_fl_nft_tc_merge *prev_nft_m_entry; 53362306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *pre_ct_entry; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci pre_ct_entry = tc_m_entry->pre_ct_parent; 53662306a36Sopenharmony_ci prev_nft_m_entry = pre_ct_entry->prev_m_entries[pre_ct_entry->num_prev_m_entries - 1]; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci return nfp_ct_merge_check(prev_nft_m_entry->nft_parent, nft_entry); 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic int nfp_ct_merge_act_check(struct nfp_fl_ct_flow_entry *pre_ct_entry, 54262306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *post_ct_entry, 54362306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *nft_entry) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct flow_action_entry *act; 54662306a36Sopenharmony_ci int i, err; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* Check for pre_ct->action conflicts */ 54962306a36Sopenharmony_ci flow_action_for_each(i, act, &pre_ct_entry->rule->action) { 55062306a36Sopenharmony_ci switch (act->id) { 55162306a36Sopenharmony_ci case FLOW_ACTION_VLAN_PUSH: 55262306a36Sopenharmony_ci case FLOW_ACTION_VLAN_POP: 55362306a36Sopenharmony_ci case FLOW_ACTION_VLAN_MANGLE: 55462306a36Sopenharmony_ci err = nfp_ct_check_vlan_merge(act, post_ct_entry->rule); 55562306a36Sopenharmony_ci if (err) 55662306a36Sopenharmony_ci return err; 55762306a36Sopenharmony_ci break; 55862306a36Sopenharmony_ci case FLOW_ACTION_MPLS_PUSH: 55962306a36Sopenharmony_ci case FLOW_ACTION_MPLS_POP: 56062306a36Sopenharmony_ci case FLOW_ACTION_MPLS_MANGLE: 56162306a36Sopenharmony_ci return -EOPNOTSUPP; 56262306a36Sopenharmony_ci default: 56362306a36Sopenharmony_ci break; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* Check for nft->action conflicts */ 56862306a36Sopenharmony_ci flow_action_for_each(i, act, &nft_entry->rule->action) { 56962306a36Sopenharmony_ci switch (act->id) { 57062306a36Sopenharmony_ci case FLOW_ACTION_VLAN_PUSH: 57162306a36Sopenharmony_ci case FLOW_ACTION_VLAN_POP: 57262306a36Sopenharmony_ci case FLOW_ACTION_VLAN_MANGLE: 57362306a36Sopenharmony_ci case FLOW_ACTION_MPLS_PUSH: 57462306a36Sopenharmony_ci case FLOW_ACTION_MPLS_POP: 57562306a36Sopenharmony_ci case FLOW_ACTION_MPLS_MANGLE: 57662306a36Sopenharmony_ci return -EOPNOTSUPP; 57762306a36Sopenharmony_ci default: 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci return 0; 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic int nfp_ct_check_meta(struct nfp_fl_ct_flow_entry *post_ct_entry, 58562306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *nft_entry) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct flow_dissector *dissector = post_ct_entry->rule->match.dissector; 58862306a36Sopenharmony_ci struct flow_action_entry *ct_met; 58962306a36Sopenharmony_ci struct flow_match_ct ct; 59062306a36Sopenharmony_ci int i; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci ct_met = get_flow_act(nft_entry->rule, FLOW_ACTION_CT_METADATA); 59362306a36Sopenharmony_ci if (ct_met && (dissector->used_keys & BIT_ULL(FLOW_DISSECTOR_KEY_CT))) { 59462306a36Sopenharmony_ci u32 *act_lbl; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci act_lbl = ct_met->ct_metadata.labels; 59762306a36Sopenharmony_ci flow_rule_match_ct(post_ct_entry->rule, &ct); 59862306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 59962306a36Sopenharmony_ci if ((ct.key->ct_labels[i] & ct.mask->ct_labels[i]) ^ 60062306a36Sopenharmony_ci (act_lbl[i] & ct.mask->ct_labels[i])) 60162306a36Sopenharmony_ci return -EINVAL; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if ((ct.key->ct_mark & ct.mask->ct_mark) ^ 60562306a36Sopenharmony_ci (ct_met->ct_metadata.mark & ct.mask->ct_mark)) 60662306a36Sopenharmony_ci return -EINVAL; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci } else { 61062306a36Sopenharmony_ci /* post_ct with ct clear action will not match the 61162306a36Sopenharmony_ci * ct status when nft is nat entry. 61262306a36Sopenharmony_ci */ 61362306a36Sopenharmony_ci if (nft_entry->flags & NFP_FL_ACTION_DO_MANGLE) 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci return -EINVAL; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic int 62162306a36Sopenharmony_cinfp_fl_calc_key_layers_sz(struct nfp_fl_key_ls in_key_ls, uint16_t *map) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci int key_size; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* This field must always be present */ 62662306a36Sopenharmony_ci key_size = sizeof(struct nfp_flower_meta_tci); 62762306a36Sopenharmony_ci map[FLOW_PAY_META_TCI] = 0; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (in_key_ls.key_layer & NFP_FLOWER_LAYER_EXT_META) { 63062306a36Sopenharmony_ci map[FLOW_PAY_EXT_META] = key_size; 63162306a36Sopenharmony_ci key_size += sizeof(struct nfp_flower_ext_meta); 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci if (in_key_ls.key_layer & NFP_FLOWER_LAYER_PORT) { 63462306a36Sopenharmony_ci map[FLOW_PAY_INPORT] = key_size; 63562306a36Sopenharmony_ci key_size += sizeof(struct nfp_flower_in_port); 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci if (in_key_ls.key_layer & NFP_FLOWER_LAYER_MAC) { 63862306a36Sopenharmony_ci map[FLOW_PAY_MAC_MPLS] = key_size; 63962306a36Sopenharmony_ci key_size += sizeof(struct nfp_flower_mac_mpls); 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci if (in_key_ls.key_layer & NFP_FLOWER_LAYER_TP) { 64262306a36Sopenharmony_ci map[FLOW_PAY_L4] = key_size; 64362306a36Sopenharmony_ci key_size += sizeof(struct nfp_flower_tp_ports); 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci if (in_key_ls.key_layer & NFP_FLOWER_LAYER_IPV4) { 64662306a36Sopenharmony_ci map[FLOW_PAY_IPV4] = key_size; 64762306a36Sopenharmony_ci key_size += sizeof(struct nfp_flower_ipv4); 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci if (in_key_ls.key_layer & NFP_FLOWER_LAYER_IPV6) { 65062306a36Sopenharmony_ci map[FLOW_PAY_IPV6] = key_size; 65162306a36Sopenharmony_ci key_size += sizeof(struct nfp_flower_ipv6); 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (in_key_ls.key_layer_two & NFP_FLOWER_LAYER2_QINQ) { 65562306a36Sopenharmony_ci map[FLOW_PAY_QINQ] = key_size; 65662306a36Sopenharmony_ci key_size += sizeof(struct nfp_flower_vlan); 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (in_key_ls.key_layer_two & NFP_FLOWER_LAYER2_GRE) { 66062306a36Sopenharmony_ci map[FLOW_PAY_GRE] = key_size; 66162306a36Sopenharmony_ci if (in_key_ls.key_layer_two & NFP_FLOWER_LAYER2_TUN_IPV6) 66262306a36Sopenharmony_ci key_size += sizeof(struct nfp_flower_ipv6_gre_tun); 66362306a36Sopenharmony_ci else 66462306a36Sopenharmony_ci key_size += sizeof(struct nfp_flower_ipv4_gre_tun); 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if ((in_key_ls.key_layer & NFP_FLOWER_LAYER_VXLAN) || 66862306a36Sopenharmony_ci (in_key_ls.key_layer_two & NFP_FLOWER_LAYER2_GENEVE)) { 66962306a36Sopenharmony_ci map[FLOW_PAY_UDP_TUN] = key_size; 67062306a36Sopenharmony_ci if (in_key_ls.key_layer_two & NFP_FLOWER_LAYER2_TUN_IPV6) 67162306a36Sopenharmony_ci key_size += sizeof(struct nfp_flower_ipv6_udp_tun); 67262306a36Sopenharmony_ci else 67362306a36Sopenharmony_ci key_size += sizeof(struct nfp_flower_ipv4_udp_tun); 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (in_key_ls.key_layer_two & NFP_FLOWER_LAYER2_GENEVE_OP) { 67762306a36Sopenharmony_ci map[FLOW_PAY_GENEVE_OPT] = key_size; 67862306a36Sopenharmony_ci key_size += sizeof(struct nfp_flower_geneve_options); 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci return key_size; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci/* get the csum flag according the ip proto and mangle action. */ 68562306a36Sopenharmony_cistatic void nfp_fl_get_csum_flag(struct flow_action_entry *a_in, u8 ip_proto, u32 *csum) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci if (a_in->id != FLOW_ACTION_MANGLE) 68862306a36Sopenharmony_ci return; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci switch (a_in->mangle.htype) { 69162306a36Sopenharmony_ci case FLOW_ACT_MANGLE_HDR_TYPE_IP4: 69262306a36Sopenharmony_ci *csum |= TCA_CSUM_UPDATE_FLAG_IPV4HDR; 69362306a36Sopenharmony_ci if (ip_proto == IPPROTO_TCP) 69462306a36Sopenharmony_ci *csum |= TCA_CSUM_UPDATE_FLAG_TCP; 69562306a36Sopenharmony_ci else if (ip_proto == IPPROTO_UDP) 69662306a36Sopenharmony_ci *csum |= TCA_CSUM_UPDATE_FLAG_UDP; 69762306a36Sopenharmony_ci break; 69862306a36Sopenharmony_ci case FLOW_ACT_MANGLE_HDR_TYPE_TCP: 69962306a36Sopenharmony_ci *csum |= TCA_CSUM_UPDATE_FLAG_TCP; 70062306a36Sopenharmony_ci break; 70162306a36Sopenharmony_ci case FLOW_ACT_MANGLE_HDR_TYPE_UDP: 70262306a36Sopenharmony_ci *csum |= TCA_CSUM_UPDATE_FLAG_UDP; 70362306a36Sopenharmony_ci break; 70462306a36Sopenharmony_ci default: 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic int nfp_fl_merge_actions_offload(struct flow_rule **rules, 71062306a36Sopenharmony_ci struct nfp_flower_priv *priv, 71162306a36Sopenharmony_ci struct net_device *netdev, 71262306a36Sopenharmony_ci struct nfp_fl_payload *flow_pay, 71362306a36Sopenharmony_ci int num_rules) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci enum flow_action_hw_stats tmp_stats = FLOW_ACTION_HW_STATS_DONT_CARE; 71662306a36Sopenharmony_ci struct flow_action_entry *a_in; 71762306a36Sopenharmony_ci int i, j, id, num_actions = 0; 71862306a36Sopenharmony_ci struct flow_rule *a_rule; 71962306a36Sopenharmony_ci int err = 0, offset = 0; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci for (i = 0; i < num_rules; i++) 72262306a36Sopenharmony_ci num_actions += rules[i]->action.num_entries; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci /* Add one action to make sure there is enough room to add an checksum action 72562306a36Sopenharmony_ci * when do nat. 72662306a36Sopenharmony_ci */ 72762306a36Sopenharmony_ci a_rule = flow_rule_alloc(num_actions + (num_rules / 2)); 72862306a36Sopenharmony_ci if (!a_rule) 72962306a36Sopenharmony_ci return -ENOMEM; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* post_ct entry have one action at least. */ 73262306a36Sopenharmony_ci if (rules[num_rules - 1]->action.num_entries != 0) 73362306a36Sopenharmony_ci tmp_stats = rules[num_rules - 1]->action.entries[0].hw_stats; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* Actions need a BASIC dissector. */ 73662306a36Sopenharmony_ci a_rule->match = rules[0]->match; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* Copy actions */ 73962306a36Sopenharmony_ci for (j = 0; j < num_rules; j++) { 74062306a36Sopenharmony_ci u32 csum_updated = 0; 74162306a36Sopenharmony_ci u8 ip_proto = 0; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (flow_rule_match_key(rules[j], FLOW_DISSECTOR_KEY_BASIC)) { 74462306a36Sopenharmony_ci struct flow_match_basic match; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* ip_proto is the only field that is needed in later compile_action, 74762306a36Sopenharmony_ci * needed to set the correct checksum flags. It doesn't really matter 74862306a36Sopenharmony_ci * which input rule's ip_proto field we take as the earlier merge checks 74962306a36Sopenharmony_ci * would have made sure that they don't conflict. We do not know which 75062306a36Sopenharmony_ci * of the subflows would have the ip_proto filled in, so we need to iterate 75162306a36Sopenharmony_ci * through the subflows and assign the proper subflow to a_rule 75262306a36Sopenharmony_ci */ 75362306a36Sopenharmony_ci flow_rule_match_basic(rules[j], &match); 75462306a36Sopenharmony_ci if (match.mask->ip_proto) { 75562306a36Sopenharmony_ci a_rule->match = rules[j]->match; 75662306a36Sopenharmony_ci ip_proto = match.key->ip_proto; 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci for (i = 0; i < rules[j]->action.num_entries; i++) { 76162306a36Sopenharmony_ci a_in = &rules[j]->action.entries[i]; 76262306a36Sopenharmony_ci id = a_in->id; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* Ignore CT related actions as these would already have 76562306a36Sopenharmony_ci * been taken care of by previous checks, and we do not send 76662306a36Sopenharmony_ci * any CT actions to the firmware. 76762306a36Sopenharmony_ci */ 76862306a36Sopenharmony_ci switch (id) { 76962306a36Sopenharmony_ci case FLOW_ACTION_CT: 77062306a36Sopenharmony_ci case FLOW_ACTION_GOTO: 77162306a36Sopenharmony_ci case FLOW_ACTION_CT_METADATA: 77262306a36Sopenharmony_ci continue; 77362306a36Sopenharmony_ci default: 77462306a36Sopenharmony_ci /* nft entry is generated by tc ct, which mangle action do not care 77562306a36Sopenharmony_ci * the stats, inherit the post entry stats to meet the 77662306a36Sopenharmony_ci * flow_action_hw_stats_check. 77762306a36Sopenharmony_ci * nft entry flow rules are at odd array index. 77862306a36Sopenharmony_ci */ 77962306a36Sopenharmony_ci if (j & 0x01) { 78062306a36Sopenharmony_ci if (a_in->hw_stats == FLOW_ACTION_HW_STATS_DONT_CARE) 78162306a36Sopenharmony_ci a_in->hw_stats = tmp_stats; 78262306a36Sopenharmony_ci nfp_fl_get_csum_flag(a_in, ip_proto, &csum_updated); 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci memcpy(&a_rule->action.entries[offset++], 78562306a36Sopenharmony_ci a_in, sizeof(struct flow_action_entry)); 78662306a36Sopenharmony_ci break; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci /* nft entry have mangle action, but do not have checksum action when do NAT, 79062306a36Sopenharmony_ci * hardware will automatically fix IPv4 and TCP/UDP checksum. so add an csum action 79162306a36Sopenharmony_ci * to meet csum action check. 79262306a36Sopenharmony_ci */ 79362306a36Sopenharmony_ci if (csum_updated) { 79462306a36Sopenharmony_ci struct flow_action_entry *csum_action; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci csum_action = &a_rule->action.entries[offset++]; 79762306a36Sopenharmony_ci csum_action->id = FLOW_ACTION_CSUM; 79862306a36Sopenharmony_ci csum_action->csum_flags = csum_updated; 79962306a36Sopenharmony_ci csum_action->hw_stats = tmp_stats; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci } 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci /* Some actions would have been ignored, so update the num_entries field */ 80462306a36Sopenharmony_ci a_rule->action.num_entries = offset; 80562306a36Sopenharmony_ci err = nfp_flower_compile_action(priv->app, a_rule, netdev, flow_pay, NULL); 80662306a36Sopenharmony_ci kfree(a_rule); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci return err; 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic int nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge *m_entry) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci enum nfp_flower_tun_type tun_type = NFP_FL_TUNNEL_NONE; 81462306a36Sopenharmony_ci struct nfp_fl_ct_zone_entry *zt = m_entry->zt; 81562306a36Sopenharmony_ci struct flow_rule *rules[NFP_MAX_ENTRY_RULES]; 81662306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *pre_ct_entry; 81762306a36Sopenharmony_ci struct nfp_fl_key_ls key_layer, tmp_layer; 81862306a36Sopenharmony_ci struct nfp_flower_priv *priv = zt->priv; 81962306a36Sopenharmony_ci u16 key_map[_FLOW_PAY_LAYERS_MAX]; 82062306a36Sopenharmony_ci struct nfp_fl_payload *flow_pay; 82162306a36Sopenharmony_ci u8 *key, *msk, *kdata, *mdata; 82262306a36Sopenharmony_ci struct nfp_port *port = NULL; 82362306a36Sopenharmony_ci int num_rules, err, i, j = 0; 82462306a36Sopenharmony_ci struct net_device *netdev; 82562306a36Sopenharmony_ci bool qinq_sup; 82662306a36Sopenharmony_ci u32 port_id; 82762306a36Sopenharmony_ci u16 offset; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci netdev = m_entry->netdev; 83062306a36Sopenharmony_ci qinq_sup = !!(priv->flower_ext_feats & NFP_FL_FEATS_VLAN_QINQ); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci pre_ct_entry = m_entry->tc_m_parent->pre_ct_parent; 83362306a36Sopenharmony_ci num_rules = pre_ct_entry->num_prev_m_entries * 2 + _CT_TYPE_MAX; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci for (i = 0; i < pre_ct_entry->num_prev_m_entries; i++) { 83662306a36Sopenharmony_ci rules[j++] = pre_ct_entry->prev_m_entries[i]->tc_m_parent->pre_ct_parent->rule; 83762306a36Sopenharmony_ci rules[j++] = pre_ct_entry->prev_m_entries[i]->nft_parent->rule; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci rules[j++] = m_entry->tc_m_parent->pre_ct_parent->rule; 84162306a36Sopenharmony_ci rules[j++] = m_entry->nft_parent->rule; 84262306a36Sopenharmony_ci rules[j++] = m_entry->tc_m_parent->post_ct_parent->rule; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci memset(&key_layer, 0, sizeof(struct nfp_fl_key_ls)); 84562306a36Sopenharmony_ci memset(&key_map, 0, sizeof(key_map)); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* Calculate the resultant key layer and size for offload */ 84862306a36Sopenharmony_ci for (i = 0; i < num_rules; i++) { 84962306a36Sopenharmony_ci err = nfp_flower_calculate_key_layers(priv->app, 85062306a36Sopenharmony_ci m_entry->netdev, 85162306a36Sopenharmony_ci &tmp_layer, rules[i], 85262306a36Sopenharmony_ci &tun_type, NULL); 85362306a36Sopenharmony_ci if (err) 85462306a36Sopenharmony_ci return err; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci key_layer.key_layer |= tmp_layer.key_layer; 85762306a36Sopenharmony_ci key_layer.key_layer_two |= tmp_layer.key_layer_two; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci key_layer.key_size = nfp_fl_calc_key_layers_sz(key_layer, key_map); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci flow_pay = nfp_flower_allocate_new(&key_layer); 86262306a36Sopenharmony_ci if (!flow_pay) 86362306a36Sopenharmony_ci return -ENOMEM; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci memset(flow_pay->unmasked_data, 0, key_layer.key_size); 86662306a36Sopenharmony_ci memset(flow_pay->mask_data, 0, key_layer.key_size); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci kdata = flow_pay->unmasked_data; 86962306a36Sopenharmony_ci mdata = flow_pay->mask_data; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci offset = key_map[FLOW_PAY_META_TCI]; 87262306a36Sopenharmony_ci key = kdata + offset; 87362306a36Sopenharmony_ci msk = mdata + offset; 87462306a36Sopenharmony_ci nfp_flower_compile_meta((struct nfp_flower_meta_tci *)key, 87562306a36Sopenharmony_ci (struct nfp_flower_meta_tci *)msk, 87662306a36Sopenharmony_ci key_layer.key_layer); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if (NFP_FLOWER_LAYER_EXT_META & key_layer.key_layer) { 87962306a36Sopenharmony_ci offset = key_map[FLOW_PAY_EXT_META]; 88062306a36Sopenharmony_ci key = kdata + offset; 88162306a36Sopenharmony_ci msk = mdata + offset; 88262306a36Sopenharmony_ci nfp_flower_compile_ext_meta((struct nfp_flower_ext_meta *)key, 88362306a36Sopenharmony_ci key_layer.key_layer_two); 88462306a36Sopenharmony_ci nfp_flower_compile_ext_meta((struct nfp_flower_ext_meta *)msk, 88562306a36Sopenharmony_ci key_layer.key_layer_two); 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci /* Using in_port from the -trk rule. The tc merge checks should already 88962306a36Sopenharmony_ci * be checking that the ingress netdevs are the same 89062306a36Sopenharmony_ci */ 89162306a36Sopenharmony_ci port_id = nfp_flower_get_port_id_from_netdev(priv->app, netdev); 89262306a36Sopenharmony_ci offset = key_map[FLOW_PAY_INPORT]; 89362306a36Sopenharmony_ci key = kdata + offset; 89462306a36Sopenharmony_ci msk = mdata + offset; 89562306a36Sopenharmony_ci err = nfp_flower_compile_port((struct nfp_flower_in_port *)key, 89662306a36Sopenharmony_ci port_id, false, tun_type, NULL); 89762306a36Sopenharmony_ci if (err) 89862306a36Sopenharmony_ci goto ct_offload_err; 89962306a36Sopenharmony_ci err = nfp_flower_compile_port((struct nfp_flower_in_port *)msk, 90062306a36Sopenharmony_ci port_id, true, tun_type, NULL); 90162306a36Sopenharmony_ci if (err) 90262306a36Sopenharmony_ci goto ct_offload_err; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci /* This following part works on the assumption that previous checks has 90562306a36Sopenharmony_ci * already filtered out flows that has different values for the different 90662306a36Sopenharmony_ci * layers. Here we iterate through all three rules and merge their respective 90762306a36Sopenharmony_ci * masked value(cared bits), basic method is: 90862306a36Sopenharmony_ci * final_key = (r1_key & r1_mask) | (r2_key & r2_mask) | (r3_key & r3_mask) 90962306a36Sopenharmony_ci * final_mask = r1_mask | r2_mask | r3_mask 91062306a36Sopenharmony_ci * If none of the rules contains a match that is also fine, that simply means 91162306a36Sopenharmony_ci * that the layer is not present. 91262306a36Sopenharmony_ci */ 91362306a36Sopenharmony_ci if (!qinq_sup) { 91462306a36Sopenharmony_ci for (i = 0; i < num_rules; i++) { 91562306a36Sopenharmony_ci offset = key_map[FLOW_PAY_META_TCI]; 91662306a36Sopenharmony_ci key = kdata + offset; 91762306a36Sopenharmony_ci msk = mdata + offset; 91862306a36Sopenharmony_ci nfp_flower_compile_tci((struct nfp_flower_meta_tci *)key, 91962306a36Sopenharmony_ci (struct nfp_flower_meta_tci *)msk, 92062306a36Sopenharmony_ci rules[i]); 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (NFP_FLOWER_LAYER_MAC & key_layer.key_layer) { 92562306a36Sopenharmony_ci offset = key_map[FLOW_PAY_MAC_MPLS]; 92662306a36Sopenharmony_ci key = kdata + offset; 92762306a36Sopenharmony_ci msk = mdata + offset; 92862306a36Sopenharmony_ci for (i = 0; i < num_rules; i++) { 92962306a36Sopenharmony_ci nfp_flower_compile_mac((struct nfp_flower_mac_mpls *)key, 93062306a36Sopenharmony_ci (struct nfp_flower_mac_mpls *)msk, 93162306a36Sopenharmony_ci rules[i]); 93262306a36Sopenharmony_ci err = nfp_flower_compile_mpls((struct nfp_flower_mac_mpls *)key, 93362306a36Sopenharmony_ci (struct nfp_flower_mac_mpls *)msk, 93462306a36Sopenharmony_ci rules[i], NULL); 93562306a36Sopenharmony_ci if (err) 93662306a36Sopenharmony_ci goto ct_offload_err; 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci if (NFP_FLOWER_LAYER_IPV4 & key_layer.key_layer) { 94162306a36Sopenharmony_ci offset = key_map[FLOW_PAY_IPV4]; 94262306a36Sopenharmony_ci key = kdata + offset; 94362306a36Sopenharmony_ci msk = mdata + offset; 94462306a36Sopenharmony_ci for (i = 0; i < num_rules; i++) { 94562306a36Sopenharmony_ci nfp_flower_compile_ipv4((struct nfp_flower_ipv4 *)key, 94662306a36Sopenharmony_ci (struct nfp_flower_ipv4 *)msk, 94762306a36Sopenharmony_ci rules[i]); 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci if (NFP_FLOWER_LAYER_IPV6 & key_layer.key_layer) { 95262306a36Sopenharmony_ci offset = key_map[FLOW_PAY_IPV6]; 95362306a36Sopenharmony_ci key = kdata + offset; 95462306a36Sopenharmony_ci msk = mdata + offset; 95562306a36Sopenharmony_ci for (i = 0; i < num_rules; i++) { 95662306a36Sopenharmony_ci nfp_flower_compile_ipv6((struct nfp_flower_ipv6 *)key, 95762306a36Sopenharmony_ci (struct nfp_flower_ipv6 *)msk, 95862306a36Sopenharmony_ci rules[i]); 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci if (NFP_FLOWER_LAYER_TP & key_layer.key_layer) { 96362306a36Sopenharmony_ci offset = key_map[FLOW_PAY_L4]; 96462306a36Sopenharmony_ci key = kdata + offset; 96562306a36Sopenharmony_ci msk = mdata + offset; 96662306a36Sopenharmony_ci for (i = 0; i < num_rules; i++) { 96762306a36Sopenharmony_ci nfp_flower_compile_tport((struct nfp_flower_tp_ports *)key, 96862306a36Sopenharmony_ci (struct nfp_flower_tp_ports *)msk, 96962306a36Sopenharmony_ci rules[i]); 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci if (NFP_FLOWER_LAYER2_QINQ & key_layer.key_layer_two) { 97462306a36Sopenharmony_ci offset = key_map[FLOW_PAY_QINQ]; 97562306a36Sopenharmony_ci key = kdata + offset; 97662306a36Sopenharmony_ci msk = mdata + offset; 97762306a36Sopenharmony_ci for (i = 0; i < num_rules; i++) { 97862306a36Sopenharmony_ci nfp_flower_compile_vlan((struct nfp_flower_vlan *)key, 97962306a36Sopenharmony_ci (struct nfp_flower_vlan *)msk, 98062306a36Sopenharmony_ci rules[i]); 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (key_layer.key_layer_two & NFP_FLOWER_LAYER2_GRE) { 98562306a36Sopenharmony_ci offset = key_map[FLOW_PAY_GRE]; 98662306a36Sopenharmony_ci key = kdata + offset; 98762306a36Sopenharmony_ci msk = mdata + offset; 98862306a36Sopenharmony_ci if (key_layer.key_layer_two & NFP_FLOWER_LAYER2_TUN_IPV6) { 98962306a36Sopenharmony_ci struct nfp_flower_ipv6_gre_tun *gre_match; 99062306a36Sopenharmony_ci struct nfp_ipv6_addr_entry *entry; 99162306a36Sopenharmony_ci struct in6_addr *dst; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci for (i = 0; i < num_rules; i++) { 99462306a36Sopenharmony_ci nfp_flower_compile_ipv6_gre_tun((void *)key, 99562306a36Sopenharmony_ci (void *)msk, rules[i]); 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci gre_match = (struct nfp_flower_ipv6_gre_tun *)key; 99862306a36Sopenharmony_ci dst = &gre_match->ipv6.dst; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci entry = nfp_tunnel_add_ipv6_off(priv->app, dst); 100162306a36Sopenharmony_ci if (!entry) { 100262306a36Sopenharmony_ci err = -ENOMEM; 100362306a36Sopenharmony_ci goto ct_offload_err; 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci flow_pay->nfp_tun_ipv6 = entry; 100762306a36Sopenharmony_ci } else { 100862306a36Sopenharmony_ci __be32 dst; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci for (i = 0; i < num_rules; i++) { 101162306a36Sopenharmony_ci nfp_flower_compile_ipv4_gre_tun((void *)key, 101262306a36Sopenharmony_ci (void *)msk, rules[i]); 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci dst = ((struct nfp_flower_ipv4_gre_tun *)key)->ipv4.dst; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci /* Store the tunnel destination in the rule data. 101762306a36Sopenharmony_ci * This must be present and be an exact match. 101862306a36Sopenharmony_ci */ 101962306a36Sopenharmony_ci flow_pay->nfp_tun_ipv4_addr = dst; 102062306a36Sopenharmony_ci nfp_tunnel_add_ipv4_off(priv->app, dst); 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci } 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci if (key_layer.key_layer & NFP_FLOWER_LAYER_VXLAN || 102562306a36Sopenharmony_ci key_layer.key_layer_two & NFP_FLOWER_LAYER2_GENEVE) { 102662306a36Sopenharmony_ci offset = key_map[FLOW_PAY_UDP_TUN]; 102762306a36Sopenharmony_ci key = kdata + offset; 102862306a36Sopenharmony_ci msk = mdata + offset; 102962306a36Sopenharmony_ci if (key_layer.key_layer_two & NFP_FLOWER_LAYER2_TUN_IPV6) { 103062306a36Sopenharmony_ci struct nfp_flower_ipv6_udp_tun *udp_match; 103162306a36Sopenharmony_ci struct nfp_ipv6_addr_entry *entry; 103262306a36Sopenharmony_ci struct in6_addr *dst; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci for (i = 0; i < num_rules; i++) { 103562306a36Sopenharmony_ci nfp_flower_compile_ipv6_udp_tun((void *)key, 103662306a36Sopenharmony_ci (void *)msk, rules[i]); 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci udp_match = (struct nfp_flower_ipv6_udp_tun *)key; 103962306a36Sopenharmony_ci dst = &udp_match->ipv6.dst; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci entry = nfp_tunnel_add_ipv6_off(priv->app, dst); 104262306a36Sopenharmony_ci if (!entry) { 104362306a36Sopenharmony_ci err = -ENOMEM; 104462306a36Sopenharmony_ci goto ct_offload_err; 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci flow_pay->nfp_tun_ipv6 = entry; 104862306a36Sopenharmony_ci } else { 104962306a36Sopenharmony_ci __be32 dst; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci for (i = 0; i < num_rules; i++) { 105262306a36Sopenharmony_ci nfp_flower_compile_ipv4_udp_tun((void *)key, 105362306a36Sopenharmony_ci (void *)msk, rules[i]); 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci dst = ((struct nfp_flower_ipv4_udp_tun *)key)->ipv4.dst; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci /* Store the tunnel destination in the rule data. 105862306a36Sopenharmony_ci * This must be present and be an exact match. 105962306a36Sopenharmony_ci */ 106062306a36Sopenharmony_ci flow_pay->nfp_tun_ipv4_addr = dst; 106162306a36Sopenharmony_ci nfp_tunnel_add_ipv4_off(priv->app, dst); 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (key_layer.key_layer_two & NFP_FLOWER_LAYER2_GENEVE_OP) { 106562306a36Sopenharmony_ci offset = key_map[FLOW_PAY_GENEVE_OPT]; 106662306a36Sopenharmony_ci key = kdata + offset; 106762306a36Sopenharmony_ci msk = mdata + offset; 106862306a36Sopenharmony_ci for (i = 0; i < num_rules; i++) 106962306a36Sopenharmony_ci nfp_flower_compile_geneve_opt(key, msk, rules[i]); 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci } 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci /* Merge actions into flow_pay */ 107462306a36Sopenharmony_ci err = nfp_fl_merge_actions_offload(rules, priv, netdev, flow_pay, num_rules); 107562306a36Sopenharmony_ci if (err) 107662306a36Sopenharmony_ci goto ct_offload_err; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci /* Use the pointer address as the cookie, but set the last bit to 1. 107962306a36Sopenharmony_ci * This is to avoid the 'is_merge_flow' check from detecting this as 108062306a36Sopenharmony_ci * an already merged flow. This works since address alignment means 108162306a36Sopenharmony_ci * that the last bit for pointer addresses will be 0. 108262306a36Sopenharmony_ci */ 108362306a36Sopenharmony_ci flow_pay->tc_flower_cookie = ((unsigned long)flow_pay) | 0x1; 108462306a36Sopenharmony_ci err = nfp_compile_flow_metadata(priv->app, flow_pay->tc_flower_cookie, 108562306a36Sopenharmony_ci flow_pay, netdev, NULL); 108662306a36Sopenharmony_ci if (err) 108762306a36Sopenharmony_ci goto ct_offload_err; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if (nfp_netdev_is_nfp_repr(netdev)) 109062306a36Sopenharmony_ci port = nfp_port_from_netdev(netdev); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci err = rhashtable_insert_fast(&priv->flow_table, &flow_pay->fl_node, 109362306a36Sopenharmony_ci nfp_flower_table_params); 109462306a36Sopenharmony_ci if (err) 109562306a36Sopenharmony_ci goto ct_release_offload_meta_err; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci err = nfp_flower_xmit_flow(priv->app, flow_pay, 109862306a36Sopenharmony_ci NFP_FLOWER_CMSG_TYPE_FLOW_ADD); 109962306a36Sopenharmony_ci if (err) 110062306a36Sopenharmony_ci goto ct_remove_rhash_err; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci m_entry->tc_flower_cookie = flow_pay->tc_flower_cookie; 110362306a36Sopenharmony_ci m_entry->flow_pay = flow_pay; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci if (port) 110662306a36Sopenharmony_ci port->tc_offload_cnt++; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci return err; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_cict_remove_rhash_err: 111162306a36Sopenharmony_ci WARN_ON_ONCE(rhashtable_remove_fast(&priv->flow_table, 111262306a36Sopenharmony_ci &flow_pay->fl_node, 111362306a36Sopenharmony_ci nfp_flower_table_params)); 111462306a36Sopenharmony_cict_release_offload_meta_err: 111562306a36Sopenharmony_ci nfp_modify_flow_metadata(priv->app, flow_pay); 111662306a36Sopenharmony_cict_offload_err: 111762306a36Sopenharmony_ci if (flow_pay->nfp_tun_ipv4_addr) 111862306a36Sopenharmony_ci nfp_tunnel_del_ipv4_off(priv->app, flow_pay->nfp_tun_ipv4_addr); 111962306a36Sopenharmony_ci if (flow_pay->nfp_tun_ipv6) 112062306a36Sopenharmony_ci nfp_tunnel_put_ipv6_off(priv->app, flow_pay->nfp_tun_ipv6); 112162306a36Sopenharmony_ci kfree(flow_pay->action_data); 112262306a36Sopenharmony_ci kfree(flow_pay->mask_data); 112362306a36Sopenharmony_ci kfree(flow_pay->unmasked_data); 112462306a36Sopenharmony_ci kfree(flow_pay); 112562306a36Sopenharmony_ci return err; 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic int nfp_fl_ct_del_offload(struct nfp_app *app, unsigned long cookie, 112962306a36Sopenharmony_ci struct net_device *netdev) 113062306a36Sopenharmony_ci{ 113162306a36Sopenharmony_ci struct nfp_flower_priv *priv = app->priv; 113262306a36Sopenharmony_ci struct nfp_fl_payload *flow_pay; 113362306a36Sopenharmony_ci struct nfp_port *port = NULL; 113462306a36Sopenharmony_ci int err = 0; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci if (nfp_netdev_is_nfp_repr(netdev)) 113762306a36Sopenharmony_ci port = nfp_port_from_netdev(netdev); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci flow_pay = nfp_flower_search_fl_table(app, cookie, netdev); 114062306a36Sopenharmony_ci if (!flow_pay) 114162306a36Sopenharmony_ci return -ENOENT; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci err = nfp_modify_flow_metadata(app, flow_pay); 114462306a36Sopenharmony_ci if (err) 114562306a36Sopenharmony_ci goto err_free_merge_flow; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci if (flow_pay->nfp_tun_ipv4_addr) 114862306a36Sopenharmony_ci nfp_tunnel_del_ipv4_off(app, flow_pay->nfp_tun_ipv4_addr); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci if (flow_pay->nfp_tun_ipv6) 115162306a36Sopenharmony_ci nfp_tunnel_put_ipv6_off(app, flow_pay->nfp_tun_ipv6); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (!flow_pay->in_hw) { 115462306a36Sopenharmony_ci err = 0; 115562306a36Sopenharmony_ci goto err_free_merge_flow; 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci err = nfp_flower_xmit_flow(app, flow_pay, 115962306a36Sopenharmony_ci NFP_FLOWER_CMSG_TYPE_FLOW_DEL); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_cierr_free_merge_flow: 116262306a36Sopenharmony_ci nfp_flower_del_linked_merge_flows(app, flow_pay); 116362306a36Sopenharmony_ci if (port) 116462306a36Sopenharmony_ci port->tc_offload_cnt--; 116562306a36Sopenharmony_ci kfree(flow_pay->action_data); 116662306a36Sopenharmony_ci kfree(flow_pay->mask_data); 116762306a36Sopenharmony_ci kfree(flow_pay->unmasked_data); 116862306a36Sopenharmony_ci WARN_ON_ONCE(rhashtable_remove_fast(&priv->flow_table, 116962306a36Sopenharmony_ci &flow_pay->fl_node, 117062306a36Sopenharmony_ci nfp_flower_table_params)); 117162306a36Sopenharmony_ci kfree_rcu(flow_pay, rcu); 117262306a36Sopenharmony_ci return err; 117362306a36Sopenharmony_ci} 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_cistatic int nfp_ct_do_nft_merge(struct nfp_fl_ct_zone_entry *zt, 117662306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *nft_entry, 117762306a36Sopenharmony_ci struct nfp_fl_ct_tc_merge *tc_m_entry) 117862306a36Sopenharmony_ci{ 117962306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *post_ct_entry, *pre_ct_entry; 118062306a36Sopenharmony_ci struct nfp_fl_nft_tc_merge *nft_m_entry; 118162306a36Sopenharmony_ci unsigned long new_cookie[3]; 118262306a36Sopenharmony_ci int err; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci pre_ct_entry = tc_m_entry->pre_ct_parent; 118562306a36Sopenharmony_ci post_ct_entry = tc_m_entry->post_ct_parent; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci err = nfp_ct_merge_act_check(pre_ct_entry, post_ct_entry, nft_entry); 118862306a36Sopenharmony_ci if (err) 118962306a36Sopenharmony_ci return err; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci /* Check that the two tc flows are also compatible with 119262306a36Sopenharmony_ci * the nft entry. No need to check the pre_ct and post_ct 119362306a36Sopenharmony_ci * entries as that was already done during pre_merge. 119462306a36Sopenharmony_ci * The nft entry does not have a chain populated, so 119562306a36Sopenharmony_ci * skip this check. 119662306a36Sopenharmony_ci */ 119762306a36Sopenharmony_ci err = nfp_ct_merge_check(pre_ct_entry, nft_entry); 119862306a36Sopenharmony_ci if (err) 119962306a36Sopenharmony_ci return err; 120062306a36Sopenharmony_ci err = nfp_ct_merge_check(nft_entry, post_ct_entry); 120162306a36Sopenharmony_ci if (err) 120262306a36Sopenharmony_ci return err; 120362306a36Sopenharmony_ci err = nfp_ct_check_meta(post_ct_entry, nft_entry); 120462306a36Sopenharmony_ci if (err) 120562306a36Sopenharmony_ci return err; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci if (pre_ct_entry->num_prev_m_entries > 0) { 120862306a36Sopenharmony_ci err = nfp_ct_merge_extra_check(nft_entry, tc_m_entry); 120962306a36Sopenharmony_ci if (err) 121062306a36Sopenharmony_ci return err; 121162306a36Sopenharmony_ci } 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci /* Combine tc_merge and nft cookies for this cookie. */ 121462306a36Sopenharmony_ci new_cookie[0] = tc_m_entry->cookie[0]; 121562306a36Sopenharmony_ci new_cookie[1] = tc_m_entry->cookie[1]; 121662306a36Sopenharmony_ci new_cookie[2] = nft_entry->cookie; 121762306a36Sopenharmony_ci nft_m_entry = get_hashentry(&zt->nft_merge_tb, 121862306a36Sopenharmony_ci &new_cookie, 121962306a36Sopenharmony_ci nfp_nft_ct_merge_params, 122062306a36Sopenharmony_ci sizeof(*nft_m_entry)); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci if (IS_ERR(nft_m_entry)) 122362306a36Sopenharmony_ci return PTR_ERR(nft_m_entry); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci /* nft_m_entry already present, not merging again */ 122662306a36Sopenharmony_ci if (!memcmp(&new_cookie, nft_m_entry->cookie, sizeof(new_cookie))) 122762306a36Sopenharmony_ci return 0; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci memcpy(&nft_m_entry->cookie, &new_cookie, sizeof(new_cookie)); 123062306a36Sopenharmony_ci nft_m_entry->zt = zt; 123162306a36Sopenharmony_ci nft_m_entry->tc_m_parent = tc_m_entry; 123262306a36Sopenharmony_ci nft_m_entry->nft_parent = nft_entry; 123362306a36Sopenharmony_ci nft_m_entry->tc_flower_cookie = 0; 123462306a36Sopenharmony_ci /* Copy the netdev from the pre_ct entry. When the tc_m_entry was created 123562306a36Sopenharmony_ci * it only combined them if the netdevs were the same, so can use any of them. 123662306a36Sopenharmony_ci */ 123762306a36Sopenharmony_ci nft_m_entry->netdev = pre_ct_entry->netdev; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci /* Add this entry to the tc_m_list and nft_flow lists */ 124062306a36Sopenharmony_ci list_add(&nft_m_entry->tc_merge_list, &tc_m_entry->children); 124162306a36Sopenharmony_ci list_add(&nft_m_entry->nft_flow_list, &nft_entry->children); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci err = rhashtable_insert_fast(&zt->nft_merge_tb, &nft_m_entry->hash_node, 124462306a36Sopenharmony_ci nfp_nft_ct_merge_params); 124562306a36Sopenharmony_ci if (err) 124662306a36Sopenharmony_ci goto err_nft_ct_merge_insert; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci zt->nft_merge_count++; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci if (post_ct_entry->goto_chain_index > 0) 125162306a36Sopenharmony_ci return nfp_fl_create_new_pre_ct(nft_m_entry); 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci /* Generate offload structure and send to nfp */ 125462306a36Sopenharmony_ci err = nfp_fl_ct_add_offload(nft_m_entry); 125562306a36Sopenharmony_ci if (err) 125662306a36Sopenharmony_ci goto err_nft_ct_offload; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci return err; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_cierr_nft_ct_offload: 126162306a36Sopenharmony_ci nfp_fl_ct_del_offload(zt->priv->app, nft_m_entry->tc_flower_cookie, 126262306a36Sopenharmony_ci nft_m_entry->netdev); 126362306a36Sopenharmony_cierr_nft_ct_merge_insert: 126462306a36Sopenharmony_ci list_del(&nft_m_entry->tc_merge_list); 126562306a36Sopenharmony_ci list_del(&nft_m_entry->nft_flow_list); 126662306a36Sopenharmony_ci kfree(nft_m_entry); 126762306a36Sopenharmony_ci return err; 126862306a36Sopenharmony_ci} 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_cistatic int nfp_ct_do_tc_merge(struct nfp_fl_ct_zone_entry *zt, 127162306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *ct_entry1, 127262306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *ct_entry2) 127362306a36Sopenharmony_ci{ 127462306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *post_ct_entry, *pre_ct_entry; 127562306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *nft_entry, *nft_tmp; 127662306a36Sopenharmony_ci struct nfp_fl_ct_tc_merge *m_entry; 127762306a36Sopenharmony_ci unsigned long new_cookie[2]; 127862306a36Sopenharmony_ci int err; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci if (ct_entry1->type == CT_TYPE_PRE_CT) { 128162306a36Sopenharmony_ci pre_ct_entry = ct_entry1; 128262306a36Sopenharmony_ci post_ct_entry = ct_entry2; 128362306a36Sopenharmony_ci } else { 128462306a36Sopenharmony_ci post_ct_entry = ct_entry1; 128562306a36Sopenharmony_ci pre_ct_entry = ct_entry2; 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci /* Checks that the chain_index of the filter matches the 128962306a36Sopenharmony_ci * chain_index of the GOTO action. 129062306a36Sopenharmony_ci */ 129162306a36Sopenharmony_ci if (post_ct_entry->chain_index != pre_ct_entry->goto_chain_index) 129262306a36Sopenharmony_ci return -EINVAL; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci err = nfp_ct_merge_check(pre_ct_entry, post_ct_entry); 129562306a36Sopenharmony_ci if (err) 129662306a36Sopenharmony_ci return err; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci new_cookie[0] = pre_ct_entry->cookie; 129962306a36Sopenharmony_ci new_cookie[1] = post_ct_entry->cookie; 130062306a36Sopenharmony_ci m_entry = get_hashentry(&zt->tc_merge_tb, &new_cookie, 130162306a36Sopenharmony_ci nfp_tc_ct_merge_params, sizeof(*m_entry)); 130262306a36Sopenharmony_ci if (IS_ERR(m_entry)) 130362306a36Sopenharmony_ci return PTR_ERR(m_entry); 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci /* m_entry already present, not merging again */ 130662306a36Sopenharmony_ci if (!memcmp(&new_cookie, m_entry->cookie, sizeof(new_cookie))) 130762306a36Sopenharmony_ci return 0; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci memcpy(&m_entry->cookie, &new_cookie, sizeof(new_cookie)); 131062306a36Sopenharmony_ci m_entry->zt = zt; 131162306a36Sopenharmony_ci m_entry->post_ct_parent = post_ct_entry; 131262306a36Sopenharmony_ci m_entry->pre_ct_parent = pre_ct_entry; 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci /* Add this entry to the pre_ct and post_ct lists */ 131562306a36Sopenharmony_ci list_add(&m_entry->post_ct_list, &post_ct_entry->children); 131662306a36Sopenharmony_ci list_add(&m_entry->pre_ct_list, &pre_ct_entry->children); 131762306a36Sopenharmony_ci INIT_LIST_HEAD(&m_entry->children); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci err = rhashtable_insert_fast(&zt->tc_merge_tb, &m_entry->hash_node, 132062306a36Sopenharmony_ci nfp_tc_ct_merge_params); 132162306a36Sopenharmony_ci if (err) 132262306a36Sopenharmony_ci goto err_ct_tc_merge_insert; 132362306a36Sopenharmony_ci zt->tc_merge_count++; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci /* Merge with existing nft flows */ 132662306a36Sopenharmony_ci list_for_each_entry_safe(nft_entry, nft_tmp, &zt->nft_flows_list, 132762306a36Sopenharmony_ci list_node) { 132862306a36Sopenharmony_ci nfp_ct_do_nft_merge(zt, nft_entry, m_entry); 132962306a36Sopenharmony_ci } 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci return 0; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_cierr_ct_tc_merge_insert: 133462306a36Sopenharmony_ci list_del(&m_entry->post_ct_list); 133562306a36Sopenharmony_ci list_del(&m_entry->pre_ct_list); 133662306a36Sopenharmony_ci kfree(m_entry); 133762306a36Sopenharmony_ci return err; 133862306a36Sopenharmony_ci} 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_cistatic struct 134162306a36Sopenharmony_cinfp_fl_ct_zone_entry *get_nfp_zone_entry(struct nfp_flower_priv *priv, 134262306a36Sopenharmony_ci u16 zone, bool wildcarded) 134362306a36Sopenharmony_ci{ 134462306a36Sopenharmony_ci struct nfp_fl_ct_zone_entry *zt; 134562306a36Sopenharmony_ci int err; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci if (wildcarded && priv->ct_zone_wc) 134862306a36Sopenharmony_ci return priv->ct_zone_wc; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci if (!wildcarded) { 135162306a36Sopenharmony_ci zt = get_hashentry(&priv->ct_zone_table, &zone, 135262306a36Sopenharmony_ci nfp_zone_table_params, sizeof(*zt)); 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci /* If priv is set this is an existing entry, just return it */ 135562306a36Sopenharmony_ci if (IS_ERR(zt) || zt->priv) 135662306a36Sopenharmony_ci return zt; 135762306a36Sopenharmony_ci } else { 135862306a36Sopenharmony_ci zt = kzalloc(sizeof(*zt), GFP_KERNEL); 135962306a36Sopenharmony_ci if (!zt) 136062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 136162306a36Sopenharmony_ci } 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci zt->zone = zone; 136462306a36Sopenharmony_ci zt->priv = priv; 136562306a36Sopenharmony_ci zt->nft = NULL; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci /* init the various hash tables and lists */ 136862306a36Sopenharmony_ci INIT_LIST_HEAD(&zt->pre_ct_list); 136962306a36Sopenharmony_ci INIT_LIST_HEAD(&zt->post_ct_list); 137062306a36Sopenharmony_ci INIT_LIST_HEAD(&zt->nft_flows_list); 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci err = rhashtable_init(&zt->tc_merge_tb, &nfp_tc_ct_merge_params); 137362306a36Sopenharmony_ci if (err) 137462306a36Sopenharmony_ci goto err_tc_merge_tb_init; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci err = rhashtable_init(&zt->nft_merge_tb, &nfp_nft_ct_merge_params); 137762306a36Sopenharmony_ci if (err) 137862306a36Sopenharmony_ci goto err_nft_merge_tb_init; 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci if (wildcarded) { 138162306a36Sopenharmony_ci priv->ct_zone_wc = zt; 138262306a36Sopenharmony_ci } else { 138362306a36Sopenharmony_ci err = rhashtable_insert_fast(&priv->ct_zone_table, 138462306a36Sopenharmony_ci &zt->hash_node, 138562306a36Sopenharmony_ci nfp_zone_table_params); 138662306a36Sopenharmony_ci if (err) 138762306a36Sopenharmony_ci goto err_zone_insert; 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci return zt; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_cierr_zone_insert: 139362306a36Sopenharmony_ci rhashtable_destroy(&zt->nft_merge_tb); 139462306a36Sopenharmony_cierr_nft_merge_tb_init: 139562306a36Sopenharmony_ci rhashtable_destroy(&zt->tc_merge_tb); 139662306a36Sopenharmony_cierr_tc_merge_tb_init: 139762306a36Sopenharmony_ci kfree(zt); 139862306a36Sopenharmony_ci return ERR_PTR(err); 139962306a36Sopenharmony_ci} 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_cistatic struct net_device *get_netdev_from_rule(struct flow_rule *rule) 140262306a36Sopenharmony_ci{ 140362306a36Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META)) { 140462306a36Sopenharmony_ci struct flow_match_meta match; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci flow_rule_match_meta(rule, &match); 140762306a36Sopenharmony_ci if (match.key->ingress_ifindex & match.mask->ingress_ifindex) 140862306a36Sopenharmony_ci return __dev_get_by_index(&init_net, 140962306a36Sopenharmony_ci match.key->ingress_ifindex); 141062306a36Sopenharmony_ci } 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci return NULL; 141362306a36Sopenharmony_ci} 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_cistatic void nfp_nft_ct_translate_mangle_action(struct flow_action_entry *mangle_action) 141662306a36Sopenharmony_ci{ 141762306a36Sopenharmony_ci if (mangle_action->id != FLOW_ACTION_MANGLE) 141862306a36Sopenharmony_ci return; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci switch (mangle_action->mangle.htype) { 142162306a36Sopenharmony_ci case FLOW_ACT_MANGLE_HDR_TYPE_IP4: 142262306a36Sopenharmony_ci case FLOW_ACT_MANGLE_HDR_TYPE_IP6: 142362306a36Sopenharmony_ci mangle_action->mangle.val = (__force u32)cpu_to_be32(mangle_action->mangle.val); 142462306a36Sopenharmony_ci mangle_action->mangle.mask = (__force u32)cpu_to_be32(mangle_action->mangle.mask); 142562306a36Sopenharmony_ci return; 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci /* Both struct tcphdr and struct udphdr start with 142862306a36Sopenharmony_ci * __be16 source; 142962306a36Sopenharmony_ci * __be16 dest; 143062306a36Sopenharmony_ci * so we can use the same code for both. 143162306a36Sopenharmony_ci */ 143262306a36Sopenharmony_ci case FLOW_ACT_MANGLE_HDR_TYPE_TCP: 143362306a36Sopenharmony_ci case FLOW_ACT_MANGLE_HDR_TYPE_UDP: 143462306a36Sopenharmony_ci if (mangle_action->mangle.offset == offsetof(struct tcphdr, source)) { 143562306a36Sopenharmony_ci mangle_action->mangle.val = 143662306a36Sopenharmony_ci (__force u32)cpu_to_be32(mangle_action->mangle.val << 16); 143762306a36Sopenharmony_ci /* The mask of mangle action is inverse mask, 143862306a36Sopenharmony_ci * so clear the dest tp port with 0xFFFF to 143962306a36Sopenharmony_ci * instead of rotate-left operation. 144062306a36Sopenharmony_ci */ 144162306a36Sopenharmony_ci mangle_action->mangle.mask = 144262306a36Sopenharmony_ci (__force u32)cpu_to_be32(mangle_action->mangle.mask << 16 | 0xFFFF); 144362306a36Sopenharmony_ci } 144462306a36Sopenharmony_ci if (mangle_action->mangle.offset == offsetof(struct tcphdr, dest)) { 144562306a36Sopenharmony_ci mangle_action->mangle.offset = 0; 144662306a36Sopenharmony_ci mangle_action->mangle.val = 144762306a36Sopenharmony_ci (__force u32)cpu_to_be32(mangle_action->mangle.val); 144862306a36Sopenharmony_ci mangle_action->mangle.mask = 144962306a36Sopenharmony_ci (__force u32)cpu_to_be32(mangle_action->mangle.mask); 145062306a36Sopenharmony_ci } 145162306a36Sopenharmony_ci return; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci default: 145462306a36Sopenharmony_ci return; 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci} 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_cistatic int nfp_nft_ct_set_flow_flag(struct flow_action_entry *act, 145962306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *entry) 146062306a36Sopenharmony_ci{ 146162306a36Sopenharmony_ci switch (act->id) { 146262306a36Sopenharmony_ci case FLOW_ACTION_CT: 146362306a36Sopenharmony_ci if (act->ct.action == TCA_CT_ACT_NAT) 146462306a36Sopenharmony_ci entry->flags |= NFP_FL_ACTION_DO_NAT; 146562306a36Sopenharmony_ci break; 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci case FLOW_ACTION_MANGLE: 146862306a36Sopenharmony_ci entry->flags |= NFP_FL_ACTION_DO_MANGLE; 146962306a36Sopenharmony_ci break; 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci default: 147262306a36Sopenharmony_ci break; 147362306a36Sopenharmony_ci } 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci return 0; 147662306a36Sopenharmony_ci} 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_cistatic struct 147962306a36Sopenharmony_cinfp_fl_ct_flow_entry *nfp_fl_ct_add_flow(struct nfp_fl_ct_zone_entry *zt, 148062306a36Sopenharmony_ci struct net_device *netdev, 148162306a36Sopenharmony_ci struct flow_cls_offload *flow, 148262306a36Sopenharmony_ci bool is_nft, struct netlink_ext_ack *extack) 148362306a36Sopenharmony_ci{ 148462306a36Sopenharmony_ci struct nf_flow_match *nft_match = NULL; 148562306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *entry; 148662306a36Sopenharmony_ci struct nfp_fl_ct_map_entry *map; 148762306a36Sopenharmony_ci struct flow_action_entry *act; 148862306a36Sopenharmony_ci int err, i; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 149162306a36Sopenharmony_ci if (!entry) 149262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci entry->rule = flow_rule_alloc(flow->rule->action.num_entries); 149562306a36Sopenharmony_ci if (!entry->rule) { 149662306a36Sopenharmony_ci err = -ENOMEM; 149762306a36Sopenharmony_ci goto err_pre_ct_rule; 149862306a36Sopenharmony_ci } 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci /* nft flows gets destroyed after callback return, so need 150162306a36Sopenharmony_ci * to do a full copy instead of just a reference. 150262306a36Sopenharmony_ci */ 150362306a36Sopenharmony_ci if (is_nft) { 150462306a36Sopenharmony_ci nft_match = kzalloc(sizeof(*nft_match), GFP_KERNEL); 150562306a36Sopenharmony_ci if (!nft_match) { 150662306a36Sopenharmony_ci err = -ENOMEM; 150762306a36Sopenharmony_ci goto err_pre_ct_act; 150862306a36Sopenharmony_ci } 150962306a36Sopenharmony_ci memcpy(&nft_match->dissector, flow->rule->match.dissector, 151062306a36Sopenharmony_ci sizeof(nft_match->dissector)); 151162306a36Sopenharmony_ci memcpy(&nft_match->mask, flow->rule->match.mask, 151262306a36Sopenharmony_ci sizeof(nft_match->mask)); 151362306a36Sopenharmony_ci memcpy(&nft_match->key, flow->rule->match.key, 151462306a36Sopenharmony_ci sizeof(nft_match->key)); 151562306a36Sopenharmony_ci entry->rule->match.dissector = &nft_match->dissector; 151662306a36Sopenharmony_ci entry->rule->match.mask = &nft_match->mask; 151762306a36Sopenharmony_ci entry->rule->match.key = &nft_match->key; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci if (!netdev) 152062306a36Sopenharmony_ci netdev = get_netdev_from_rule(entry->rule); 152162306a36Sopenharmony_ci } else { 152262306a36Sopenharmony_ci entry->rule->match.dissector = flow->rule->match.dissector; 152362306a36Sopenharmony_ci entry->rule->match.mask = flow->rule->match.mask; 152462306a36Sopenharmony_ci entry->rule->match.key = flow->rule->match.key; 152562306a36Sopenharmony_ci } 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci entry->zt = zt; 152862306a36Sopenharmony_ci entry->netdev = netdev; 152962306a36Sopenharmony_ci entry->cookie = flow->cookie > 0 ? flow->cookie : (unsigned long)entry; 153062306a36Sopenharmony_ci entry->chain_index = flow->common.chain_index; 153162306a36Sopenharmony_ci entry->tun_offset = NFP_FL_CT_NO_TUN; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci /* Copy over action data. Unfortunately we do not get a handle to the 153462306a36Sopenharmony_ci * original tcf_action data, and the flow objects gets destroyed, so we 153562306a36Sopenharmony_ci * cannot just save a pointer to this either, so need to copy over the 153662306a36Sopenharmony_ci * data unfortunately. 153762306a36Sopenharmony_ci */ 153862306a36Sopenharmony_ci entry->rule->action.num_entries = flow->rule->action.num_entries; 153962306a36Sopenharmony_ci flow_action_for_each(i, act, &flow->rule->action) { 154062306a36Sopenharmony_ci struct flow_action_entry *new_act; 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci new_act = &entry->rule->action.entries[i]; 154362306a36Sopenharmony_ci memcpy(new_act, act, sizeof(struct flow_action_entry)); 154462306a36Sopenharmony_ci /* nft entry mangle field is host byte order, need translate to 154562306a36Sopenharmony_ci * network byte order. 154662306a36Sopenharmony_ci */ 154762306a36Sopenharmony_ci if (is_nft) 154862306a36Sopenharmony_ci nfp_nft_ct_translate_mangle_action(new_act); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci nfp_nft_ct_set_flow_flag(new_act, entry); 155162306a36Sopenharmony_ci /* Entunnel is a special case, need to allocate and copy 155262306a36Sopenharmony_ci * tunnel info. 155362306a36Sopenharmony_ci */ 155462306a36Sopenharmony_ci if (act->id == FLOW_ACTION_TUNNEL_ENCAP) { 155562306a36Sopenharmony_ci struct ip_tunnel_info *tun = act->tunnel; 155662306a36Sopenharmony_ci size_t tun_size = sizeof(*tun) + tun->options_len; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci new_act->tunnel = kmemdup(tun, tun_size, GFP_ATOMIC); 155962306a36Sopenharmony_ci if (!new_act->tunnel) { 156062306a36Sopenharmony_ci err = -ENOMEM; 156162306a36Sopenharmony_ci goto err_pre_ct_tun_cp; 156262306a36Sopenharmony_ci } 156362306a36Sopenharmony_ci entry->tun_offset = i; 156462306a36Sopenharmony_ci } 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci INIT_LIST_HEAD(&entry->children); 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci if (flow->cookie == 0) 157062306a36Sopenharmony_ci return entry; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci /* Now add a ct map entry to flower-priv */ 157362306a36Sopenharmony_ci map = get_hashentry(&zt->priv->ct_map_table, &flow->cookie, 157462306a36Sopenharmony_ci nfp_ct_map_params, sizeof(*map)); 157562306a36Sopenharmony_ci if (IS_ERR(map)) { 157662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 157762306a36Sopenharmony_ci "offload error: ct map entry creation failed"); 157862306a36Sopenharmony_ci err = -ENOMEM; 157962306a36Sopenharmony_ci goto err_ct_flow_insert; 158062306a36Sopenharmony_ci } 158162306a36Sopenharmony_ci map->cookie = flow->cookie; 158262306a36Sopenharmony_ci map->ct_entry = entry; 158362306a36Sopenharmony_ci err = rhashtable_insert_fast(&zt->priv->ct_map_table, 158462306a36Sopenharmony_ci &map->hash_node, 158562306a36Sopenharmony_ci nfp_ct_map_params); 158662306a36Sopenharmony_ci if (err) { 158762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 158862306a36Sopenharmony_ci "offload error: ct map entry table add failed"); 158962306a36Sopenharmony_ci goto err_map_insert; 159062306a36Sopenharmony_ci } 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci return entry; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_cierr_map_insert: 159562306a36Sopenharmony_ci kfree(map); 159662306a36Sopenharmony_cierr_ct_flow_insert: 159762306a36Sopenharmony_ci if (entry->tun_offset != NFP_FL_CT_NO_TUN) 159862306a36Sopenharmony_ci kfree(entry->rule->action.entries[entry->tun_offset].tunnel); 159962306a36Sopenharmony_cierr_pre_ct_tun_cp: 160062306a36Sopenharmony_ci kfree(nft_match); 160162306a36Sopenharmony_cierr_pre_ct_act: 160262306a36Sopenharmony_ci kfree(entry->rule); 160362306a36Sopenharmony_cierr_pre_ct_rule: 160462306a36Sopenharmony_ci kfree(entry); 160562306a36Sopenharmony_ci return ERR_PTR(err); 160662306a36Sopenharmony_ci} 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_cistatic void cleanup_nft_merge_entry(struct nfp_fl_nft_tc_merge *m_entry) 160962306a36Sopenharmony_ci{ 161062306a36Sopenharmony_ci struct nfp_fl_ct_zone_entry *zt; 161162306a36Sopenharmony_ci int err; 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci zt = m_entry->zt; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci /* Flow is in HW, need to delete */ 161662306a36Sopenharmony_ci if (m_entry->tc_flower_cookie) { 161762306a36Sopenharmony_ci err = nfp_fl_ct_del_offload(zt->priv->app, m_entry->tc_flower_cookie, 161862306a36Sopenharmony_ci m_entry->netdev); 161962306a36Sopenharmony_ci if (err) 162062306a36Sopenharmony_ci return; 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci WARN_ON_ONCE(rhashtable_remove_fast(&zt->nft_merge_tb, 162462306a36Sopenharmony_ci &m_entry->hash_node, 162562306a36Sopenharmony_ci nfp_nft_ct_merge_params)); 162662306a36Sopenharmony_ci zt->nft_merge_count--; 162762306a36Sopenharmony_ci list_del(&m_entry->tc_merge_list); 162862306a36Sopenharmony_ci list_del(&m_entry->nft_flow_list); 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci if (m_entry->next_pre_ct_entry) { 163162306a36Sopenharmony_ci struct nfp_fl_ct_map_entry pre_ct_map_ent; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci pre_ct_map_ent.ct_entry = m_entry->next_pre_ct_entry; 163462306a36Sopenharmony_ci pre_ct_map_ent.cookie = 0; 163562306a36Sopenharmony_ci nfp_fl_ct_del_flow(&pre_ct_map_ent); 163662306a36Sopenharmony_ci } 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci kfree(m_entry); 163962306a36Sopenharmony_ci} 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_cistatic void nfp_free_nft_merge_children(void *entry, bool is_nft_flow) 164262306a36Sopenharmony_ci{ 164362306a36Sopenharmony_ci struct nfp_fl_nft_tc_merge *m_entry, *tmp; 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci /* These post entries are parts of two lists, one is a list of nft_entries 164662306a36Sopenharmony_ci * and the other is of from a list of tc_merge structures. Iterate 164762306a36Sopenharmony_ci * through the relevant list and cleanup the entries. 164862306a36Sopenharmony_ci */ 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci if (is_nft_flow) { 165162306a36Sopenharmony_ci /* Need to iterate through list of nft_flow entries */ 165262306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *ct_entry = entry; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci list_for_each_entry_safe(m_entry, tmp, &ct_entry->children, 165562306a36Sopenharmony_ci nft_flow_list) { 165662306a36Sopenharmony_ci cleanup_nft_merge_entry(m_entry); 165762306a36Sopenharmony_ci } 165862306a36Sopenharmony_ci } else { 165962306a36Sopenharmony_ci /* Need to iterate through list of tc_merged_flow entries */ 166062306a36Sopenharmony_ci struct nfp_fl_ct_tc_merge *ct_entry = entry; 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci list_for_each_entry_safe(m_entry, tmp, &ct_entry->children, 166362306a36Sopenharmony_ci tc_merge_list) { 166462306a36Sopenharmony_ci cleanup_nft_merge_entry(m_entry); 166562306a36Sopenharmony_ci } 166662306a36Sopenharmony_ci } 166762306a36Sopenharmony_ci} 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_cistatic void nfp_del_tc_merge_entry(struct nfp_fl_ct_tc_merge *m_ent) 167062306a36Sopenharmony_ci{ 167162306a36Sopenharmony_ci struct nfp_fl_ct_zone_entry *zt; 167262306a36Sopenharmony_ci int err; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci zt = m_ent->zt; 167562306a36Sopenharmony_ci err = rhashtable_remove_fast(&zt->tc_merge_tb, 167662306a36Sopenharmony_ci &m_ent->hash_node, 167762306a36Sopenharmony_ci nfp_tc_ct_merge_params); 167862306a36Sopenharmony_ci if (err) 167962306a36Sopenharmony_ci pr_warn("WARNING: could not remove merge_entry from hashtable\n"); 168062306a36Sopenharmony_ci zt->tc_merge_count--; 168162306a36Sopenharmony_ci list_del(&m_ent->post_ct_list); 168262306a36Sopenharmony_ci list_del(&m_ent->pre_ct_list); 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci if (!list_empty(&m_ent->children)) 168562306a36Sopenharmony_ci nfp_free_nft_merge_children(m_ent, false); 168662306a36Sopenharmony_ci kfree(m_ent); 168762306a36Sopenharmony_ci} 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_cistatic void nfp_free_tc_merge_children(struct nfp_fl_ct_flow_entry *entry) 169062306a36Sopenharmony_ci{ 169162306a36Sopenharmony_ci struct nfp_fl_ct_tc_merge *m_ent, *tmp; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci switch (entry->type) { 169462306a36Sopenharmony_ci case CT_TYPE_PRE_CT: 169562306a36Sopenharmony_ci list_for_each_entry_safe(m_ent, tmp, &entry->children, pre_ct_list) { 169662306a36Sopenharmony_ci nfp_del_tc_merge_entry(m_ent); 169762306a36Sopenharmony_ci } 169862306a36Sopenharmony_ci break; 169962306a36Sopenharmony_ci case CT_TYPE_POST_CT: 170062306a36Sopenharmony_ci list_for_each_entry_safe(m_ent, tmp, &entry->children, post_ct_list) { 170162306a36Sopenharmony_ci nfp_del_tc_merge_entry(m_ent); 170262306a36Sopenharmony_ci } 170362306a36Sopenharmony_ci break; 170462306a36Sopenharmony_ci default: 170562306a36Sopenharmony_ci break; 170662306a36Sopenharmony_ci } 170762306a36Sopenharmony_ci} 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_civoid nfp_fl_ct_clean_flow_entry(struct nfp_fl_ct_flow_entry *entry) 171062306a36Sopenharmony_ci{ 171162306a36Sopenharmony_ci list_del(&entry->list_node); 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci if (!list_empty(&entry->children)) { 171462306a36Sopenharmony_ci if (entry->type == CT_TYPE_NFT) 171562306a36Sopenharmony_ci nfp_free_nft_merge_children(entry, true); 171662306a36Sopenharmony_ci else 171762306a36Sopenharmony_ci nfp_free_tc_merge_children(entry); 171862306a36Sopenharmony_ci } 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci if (entry->tun_offset != NFP_FL_CT_NO_TUN) 172162306a36Sopenharmony_ci kfree(entry->rule->action.entries[entry->tun_offset].tunnel); 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci if (entry->type == CT_TYPE_NFT) { 172462306a36Sopenharmony_ci struct nf_flow_match *nft_match; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci nft_match = container_of(entry->rule->match.dissector, 172762306a36Sopenharmony_ci struct nf_flow_match, dissector); 172862306a36Sopenharmony_ci kfree(nft_match); 172962306a36Sopenharmony_ci } 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci kfree(entry->rule); 173262306a36Sopenharmony_ci kfree(entry); 173362306a36Sopenharmony_ci} 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_cistatic struct flow_action_entry *get_flow_act_ct(struct flow_rule *rule) 173662306a36Sopenharmony_ci{ 173762306a36Sopenharmony_ci struct flow_action_entry *act; 173862306a36Sopenharmony_ci int i; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci /* More than one ct action may be present in a flow rule, 174162306a36Sopenharmony_ci * Return the first one that is not a CT clear action 174262306a36Sopenharmony_ci */ 174362306a36Sopenharmony_ci flow_action_for_each(i, act, &rule->action) { 174462306a36Sopenharmony_ci if (act->id == FLOW_ACTION_CT && act->ct.action != TCA_CT_ACT_CLEAR) 174562306a36Sopenharmony_ci return act; 174662306a36Sopenharmony_ci } 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci return NULL; 174962306a36Sopenharmony_ci} 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_cistatic struct flow_action_entry *get_flow_act(struct flow_rule *rule, 175262306a36Sopenharmony_ci enum flow_action_id act_id) 175362306a36Sopenharmony_ci{ 175462306a36Sopenharmony_ci struct flow_action_entry *act = NULL; 175562306a36Sopenharmony_ci int i; 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci flow_action_for_each(i, act, &rule->action) { 175862306a36Sopenharmony_ci if (act->id == act_id) 175962306a36Sopenharmony_ci return act; 176062306a36Sopenharmony_ci } 176162306a36Sopenharmony_ci return NULL; 176262306a36Sopenharmony_ci} 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_cistatic void 176562306a36Sopenharmony_cinfp_ct_merge_tc_entries(struct nfp_fl_ct_flow_entry *ct_entry1, 176662306a36Sopenharmony_ci struct nfp_fl_ct_zone_entry *zt_src, 176762306a36Sopenharmony_ci struct nfp_fl_ct_zone_entry *zt_dst) 176862306a36Sopenharmony_ci{ 176962306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *ct_entry2, *ct_tmp; 177062306a36Sopenharmony_ci struct list_head *ct_list; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci if (ct_entry1->type == CT_TYPE_PRE_CT) 177362306a36Sopenharmony_ci ct_list = &zt_src->post_ct_list; 177462306a36Sopenharmony_ci else if (ct_entry1->type == CT_TYPE_POST_CT) 177562306a36Sopenharmony_ci ct_list = &zt_src->pre_ct_list; 177662306a36Sopenharmony_ci else 177762306a36Sopenharmony_ci return; 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci list_for_each_entry_safe(ct_entry2, ct_tmp, ct_list, 178062306a36Sopenharmony_ci list_node) { 178162306a36Sopenharmony_ci nfp_ct_do_tc_merge(zt_dst, ct_entry2, ct_entry1); 178262306a36Sopenharmony_ci } 178362306a36Sopenharmony_ci} 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_cistatic void 178662306a36Sopenharmony_cinfp_ct_merge_nft_with_tc(struct nfp_fl_ct_flow_entry *nft_entry, 178762306a36Sopenharmony_ci struct nfp_fl_ct_zone_entry *zt) 178862306a36Sopenharmony_ci{ 178962306a36Sopenharmony_ci struct nfp_fl_ct_tc_merge *tc_merge_entry; 179062306a36Sopenharmony_ci struct rhashtable_iter iter; 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci rhashtable_walk_enter(&zt->tc_merge_tb, &iter); 179362306a36Sopenharmony_ci rhashtable_walk_start(&iter); 179462306a36Sopenharmony_ci while ((tc_merge_entry = rhashtable_walk_next(&iter)) != NULL) { 179562306a36Sopenharmony_ci if (IS_ERR(tc_merge_entry)) 179662306a36Sopenharmony_ci continue; 179762306a36Sopenharmony_ci rhashtable_walk_stop(&iter); 179862306a36Sopenharmony_ci nfp_ct_do_nft_merge(zt, nft_entry, tc_merge_entry); 179962306a36Sopenharmony_ci rhashtable_walk_start(&iter); 180062306a36Sopenharmony_ci } 180162306a36Sopenharmony_ci rhashtable_walk_stop(&iter); 180262306a36Sopenharmony_ci rhashtable_walk_exit(&iter); 180362306a36Sopenharmony_ci} 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ciint nfp_fl_ct_handle_pre_ct(struct nfp_flower_priv *priv, 180662306a36Sopenharmony_ci struct net_device *netdev, 180762306a36Sopenharmony_ci struct flow_cls_offload *flow, 180862306a36Sopenharmony_ci struct netlink_ext_ack *extack, 180962306a36Sopenharmony_ci struct nfp_fl_nft_tc_merge *m_entry) 181062306a36Sopenharmony_ci{ 181162306a36Sopenharmony_ci struct flow_action_entry *ct_act, *ct_goto; 181262306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *ct_entry; 181362306a36Sopenharmony_ci struct nfp_fl_ct_zone_entry *zt; 181462306a36Sopenharmony_ci int err; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci ct_act = get_flow_act_ct(flow->rule); 181762306a36Sopenharmony_ci if (!ct_act) { 181862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 181962306a36Sopenharmony_ci "unsupported offload: Conntrack action empty in conntrack offload"); 182062306a36Sopenharmony_ci return -EOPNOTSUPP; 182162306a36Sopenharmony_ci } 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci ct_goto = get_flow_act(flow->rule, FLOW_ACTION_GOTO); 182462306a36Sopenharmony_ci if (!ct_goto) { 182562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 182662306a36Sopenharmony_ci "unsupported offload: Conntrack requires ACTION_GOTO"); 182762306a36Sopenharmony_ci return -EOPNOTSUPP; 182862306a36Sopenharmony_ci } 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci zt = get_nfp_zone_entry(priv, ct_act->ct.zone, false); 183162306a36Sopenharmony_ci if (IS_ERR(zt)) { 183262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 183362306a36Sopenharmony_ci "offload error: Could not create zone table entry"); 183462306a36Sopenharmony_ci return PTR_ERR(zt); 183562306a36Sopenharmony_ci } 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci if (!zt->nft) { 183862306a36Sopenharmony_ci zt->nft = ct_act->ct.flow_table; 183962306a36Sopenharmony_ci err = nf_flow_table_offload_add_cb(zt->nft, nfp_fl_ct_handle_nft_flow, zt); 184062306a36Sopenharmony_ci if (err) { 184162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 184262306a36Sopenharmony_ci "offload error: Could not register nft_callback"); 184362306a36Sopenharmony_ci return err; 184462306a36Sopenharmony_ci } 184562306a36Sopenharmony_ci } 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci /* Add entry to pre_ct_list */ 184862306a36Sopenharmony_ci ct_entry = nfp_fl_ct_add_flow(zt, netdev, flow, false, extack); 184962306a36Sopenharmony_ci if (IS_ERR(ct_entry)) 185062306a36Sopenharmony_ci return PTR_ERR(ct_entry); 185162306a36Sopenharmony_ci ct_entry->type = CT_TYPE_PRE_CT; 185262306a36Sopenharmony_ci ct_entry->chain_index = flow->common.chain_index; 185362306a36Sopenharmony_ci ct_entry->goto_chain_index = ct_goto->chain_index; 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci if (m_entry) { 185662306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *pre_ct_entry; 185762306a36Sopenharmony_ci int i; 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci pre_ct_entry = m_entry->tc_m_parent->pre_ct_parent; 186062306a36Sopenharmony_ci for (i = 0; i < pre_ct_entry->num_prev_m_entries; i++) 186162306a36Sopenharmony_ci ct_entry->prev_m_entries[i] = pre_ct_entry->prev_m_entries[i]; 186262306a36Sopenharmony_ci ct_entry->prev_m_entries[i++] = m_entry; 186362306a36Sopenharmony_ci ct_entry->num_prev_m_entries = i; 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci m_entry->next_pre_ct_entry = ct_entry; 186662306a36Sopenharmony_ci } 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci list_add(&ct_entry->list_node, &zt->pre_ct_list); 186962306a36Sopenharmony_ci zt->pre_ct_count++; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci nfp_ct_merge_tc_entries(ct_entry, zt, zt); 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci /* Need to check and merge with tables in the wc_zone as well */ 187462306a36Sopenharmony_ci if (priv->ct_zone_wc) 187562306a36Sopenharmony_ci nfp_ct_merge_tc_entries(ct_entry, priv->ct_zone_wc, zt); 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci return 0; 187862306a36Sopenharmony_ci} 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ciint nfp_fl_ct_handle_post_ct(struct nfp_flower_priv *priv, 188162306a36Sopenharmony_ci struct net_device *netdev, 188262306a36Sopenharmony_ci struct flow_cls_offload *flow, 188362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 188462306a36Sopenharmony_ci{ 188562306a36Sopenharmony_ci struct flow_rule *rule = flow_cls_offload_flow_rule(flow); 188662306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *ct_entry; 188762306a36Sopenharmony_ci struct flow_action_entry *ct_goto; 188862306a36Sopenharmony_ci struct nfp_fl_ct_zone_entry *zt; 188962306a36Sopenharmony_ci struct flow_action_entry *act; 189062306a36Sopenharmony_ci bool wildcarded = false; 189162306a36Sopenharmony_ci struct flow_match_ct ct; 189262306a36Sopenharmony_ci int i; 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci flow_action_for_each(i, act, &rule->action) { 189562306a36Sopenharmony_ci switch (act->id) { 189662306a36Sopenharmony_ci case FLOW_ACTION_REDIRECT: 189762306a36Sopenharmony_ci case FLOW_ACTION_REDIRECT_INGRESS: 189862306a36Sopenharmony_ci case FLOW_ACTION_MIRRED: 189962306a36Sopenharmony_ci case FLOW_ACTION_MIRRED_INGRESS: 190062306a36Sopenharmony_ci if (act->dev->rtnl_link_ops && 190162306a36Sopenharmony_ci !strcmp(act->dev->rtnl_link_ops->kind, "openvswitch")) { 190262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 190362306a36Sopenharmony_ci "unsupported offload: out port is openvswitch internal port"); 190462306a36Sopenharmony_ci return -EOPNOTSUPP; 190562306a36Sopenharmony_ci } 190662306a36Sopenharmony_ci break; 190762306a36Sopenharmony_ci default: 190862306a36Sopenharmony_ci break; 190962306a36Sopenharmony_ci } 191062306a36Sopenharmony_ci } 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci flow_rule_match_ct(rule, &ct); 191362306a36Sopenharmony_ci if (!ct.mask->ct_zone) { 191462306a36Sopenharmony_ci wildcarded = true; 191562306a36Sopenharmony_ci } else if (ct.mask->ct_zone != U16_MAX) { 191662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 191762306a36Sopenharmony_ci "unsupported offload: partially wildcarded ct_zone is not supported"); 191862306a36Sopenharmony_ci return -EOPNOTSUPP; 191962306a36Sopenharmony_ci } 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci zt = get_nfp_zone_entry(priv, ct.key->ct_zone, wildcarded); 192262306a36Sopenharmony_ci if (IS_ERR(zt)) { 192362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 192462306a36Sopenharmony_ci "offload error: Could not create zone table entry"); 192562306a36Sopenharmony_ci return PTR_ERR(zt); 192662306a36Sopenharmony_ci } 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci /* Add entry to post_ct_list */ 192962306a36Sopenharmony_ci ct_entry = nfp_fl_ct_add_flow(zt, netdev, flow, false, extack); 193062306a36Sopenharmony_ci if (IS_ERR(ct_entry)) 193162306a36Sopenharmony_ci return PTR_ERR(ct_entry); 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci ct_entry->type = CT_TYPE_POST_CT; 193462306a36Sopenharmony_ci ct_entry->chain_index = flow->common.chain_index; 193562306a36Sopenharmony_ci ct_goto = get_flow_act(flow->rule, FLOW_ACTION_GOTO); 193662306a36Sopenharmony_ci ct_entry->goto_chain_index = ct_goto ? ct_goto->chain_index : 0; 193762306a36Sopenharmony_ci list_add(&ct_entry->list_node, &zt->post_ct_list); 193862306a36Sopenharmony_ci zt->post_ct_count++; 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci if (wildcarded) { 194162306a36Sopenharmony_ci /* Iterate through all zone tables if not empty, look for merges with 194262306a36Sopenharmony_ci * pre_ct entries and merge them. 194362306a36Sopenharmony_ci */ 194462306a36Sopenharmony_ci struct rhashtable_iter iter; 194562306a36Sopenharmony_ci struct nfp_fl_ct_zone_entry *zone_table; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci rhashtable_walk_enter(&priv->ct_zone_table, &iter); 194862306a36Sopenharmony_ci rhashtable_walk_start(&iter); 194962306a36Sopenharmony_ci while ((zone_table = rhashtable_walk_next(&iter)) != NULL) { 195062306a36Sopenharmony_ci if (IS_ERR(zone_table)) 195162306a36Sopenharmony_ci continue; 195262306a36Sopenharmony_ci rhashtable_walk_stop(&iter); 195362306a36Sopenharmony_ci nfp_ct_merge_tc_entries(ct_entry, zone_table, zone_table); 195462306a36Sopenharmony_ci rhashtable_walk_start(&iter); 195562306a36Sopenharmony_ci } 195662306a36Sopenharmony_ci rhashtable_walk_stop(&iter); 195762306a36Sopenharmony_ci rhashtable_walk_exit(&iter); 195862306a36Sopenharmony_ci } else { 195962306a36Sopenharmony_ci nfp_ct_merge_tc_entries(ct_entry, zt, zt); 196062306a36Sopenharmony_ci } 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci return 0; 196362306a36Sopenharmony_ci} 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ciint nfp_fl_create_new_pre_ct(struct nfp_fl_nft_tc_merge *m_entry) 196662306a36Sopenharmony_ci{ 196762306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *pre_ct_entry, *post_ct_entry; 196862306a36Sopenharmony_ci struct flow_cls_offload new_pre_ct_flow; 196962306a36Sopenharmony_ci int err; 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci pre_ct_entry = m_entry->tc_m_parent->pre_ct_parent; 197262306a36Sopenharmony_ci if (pre_ct_entry->num_prev_m_entries >= NFP_MAX_RECIRC_CT_ZONES - 1) 197362306a36Sopenharmony_ci return -1; 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci post_ct_entry = m_entry->tc_m_parent->post_ct_parent; 197662306a36Sopenharmony_ci memset(&new_pre_ct_flow, 0, sizeof(struct flow_cls_offload)); 197762306a36Sopenharmony_ci new_pre_ct_flow.rule = post_ct_entry->rule; 197862306a36Sopenharmony_ci new_pre_ct_flow.common.chain_index = post_ct_entry->chain_index; 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci err = nfp_fl_ct_handle_pre_ct(pre_ct_entry->zt->priv, 198162306a36Sopenharmony_ci pre_ct_entry->netdev, 198262306a36Sopenharmony_ci &new_pre_ct_flow, NULL, 198362306a36Sopenharmony_ci m_entry); 198462306a36Sopenharmony_ci return err; 198562306a36Sopenharmony_ci} 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_cistatic void 198862306a36Sopenharmony_cinfp_fl_ct_sub_stats(struct nfp_fl_nft_tc_merge *nft_merge, 198962306a36Sopenharmony_ci enum ct_entry_type type, u64 *m_pkts, 199062306a36Sopenharmony_ci u64 *m_bytes, u64 *m_used) 199162306a36Sopenharmony_ci{ 199262306a36Sopenharmony_ci struct nfp_flower_priv *priv = nft_merge->zt->priv; 199362306a36Sopenharmony_ci struct nfp_fl_payload *nfp_flow; 199462306a36Sopenharmony_ci u32 ctx_id; 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci nfp_flow = nft_merge->flow_pay; 199762306a36Sopenharmony_ci if (!nfp_flow) 199862306a36Sopenharmony_ci return; 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci ctx_id = be32_to_cpu(nfp_flow->meta.host_ctx_id); 200162306a36Sopenharmony_ci *m_pkts += priv->stats[ctx_id].pkts; 200262306a36Sopenharmony_ci *m_bytes += priv->stats[ctx_id].bytes; 200362306a36Sopenharmony_ci *m_used = max_t(u64, *m_used, priv->stats[ctx_id].used); 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci /* If request is for a sub_flow which is part of a tunnel merged 200662306a36Sopenharmony_ci * flow then update stats from tunnel merged flows first. 200762306a36Sopenharmony_ci */ 200862306a36Sopenharmony_ci if (!list_empty(&nfp_flow->linked_flows)) 200962306a36Sopenharmony_ci nfp_flower_update_merge_stats(priv->app, nfp_flow); 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ci if (type != CT_TYPE_NFT) { 201262306a36Sopenharmony_ci /* Update nft cached stats */ 201362306a36Sopenharmony_ci flow_stats_update(&nft_merge->nft_parent->stats, 201462306a36Sopenharmony_ci priv->stats[ctx_id].bytes, 201562306a36Sopenharmony_ci priv->stats[ctx_id].pkts, 201662306a36Sopenharmony_ci 0, priv->stats[ctx_id].used, 201762306a36Sopenharmony_ci FLOW_ACTION_HW_STATS_DELAYED); 201862306a36Sopenharmony_ci } else { 201962306a36Sopenharmony_ci /* Update pre_ct cached stats */ 202062306a36Sopenharmony_ci flow_stats_update(&nft_merge->tc_m_parent->pre_ct_parent->stats, 202162306a36Sopenharmony_ci priv->stats[ctx_id].bytes, 202262306a36Sopenharmony_ci priv->stats[ctx_id].pkts, 202362306a36Sopenharmony_ci 0, priv->stats[ctx_id].used, 202462306a36Sopenharmony_ci FLOW_ACTION_HW_STATS_DELAYED); 202562306a36Sopenharmony_ci /* Update post_ct cached stats */ 202662306a36Sopenharmony_ci flow_stats_update(&nft_merge->tc_m_parent->post_ct_parent->stats, 202762306a36Sopenharmony_ci priv->stats[ctx_id].bytes, 202862306a36Sopenharmony_ci priv->stats[ctx_id].pkts, 202962306a36Sopenharmony_ci 0, priv->stats[ctx_id].used, 203062306a36Sopenharmony_ci FLOW_ACTION_HW_STATS_DELAYED); 203162306a36Sopenharmony_ci } 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci /* Update previous pre_ct/post_ct/nft flow stats */ 203462306a36Sopenharmony_ci if (nft_merge->tc_m_parent->pre_ct_parent->num_prev_m_entries > 0) { 203562306a36Sopenharmony_ci struct nfp_fl_nft_tc_merge *tmp_nft_merge; 203662306a36Sopenharmony_ci int i; 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci for (i = 0; i < nft_merge->tc_m_parent->pre_ct_parent->num_prev_m_entries; i++) { 203962306a36Sopenharmony_ci tmp_nft_merge = nft_merge->tc_m_parent->pre_ct_parent->prev_m_entries[i]; 204062306a36Sopenharmony_ci flow_stats_update(&tmp_nft_merge->tc_m_parent->pre_ct_parent->stats, 204162306a36Sopenharmony_ci priv->stats[ctx_id].bytes, 204262306a36Sopenharmony_ci priv->stats[ctx_id].pkts, 204362306a36Sopenharmony_ci 0, priv->stats[ctx_id].used, 204462306a36Sopenharmony_ci FLOW_ACTION_HW_STATS_DELAYED); 204562306a36Sopenharmony_ci flow_stats_update(&tmp_nft_merge->tc_m_parent->post_ct_parent->stats, 204662306a36Sopenharmony_ci priv->stats[ctx_id].bytes, 204762306a36Sopenharmony_ci priv->stats[ctx_id].pkts, 204862306a36Sopenharmony_ci 0, priv->stats[ctx_id].used, 204962306a36Sopenharmony_ci FLOW_ACTION_HW_STATS_DELAYED); 205062306a36Sopenharmony_ci flow_stats_update(&tmp_nft_merge->nft_parent->stats, 205162306a36Sopenharmony_ci priv->stats[ctx_id].bytes, 205262306a36Sopenharmony_ci priv->stats[ctx_id].pkts, 205362306a36Sopenharmony_ci 0, priv->stats[ctx_id].used, 205462306a36Sopenharmony_ci FLOW_ACTION_HW_STATS_DELAYED); 205562306a36Sopenharmony_ci } 205662306a36Sopenharmony_ci } 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci /* Reset stats from the nfp */ 205962306a36Sopenharmony_ci priv->stats[ctx_id].pkts = 0; 206062306a36Sopenharmony_ci priv->stats[ctx_id].bytes = 0; 206162306a36Sopenharmony_ci} 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ciint nfp_fl_ct_stats(struct flow_cls_offload *flow, 206462306a36Sopenharmony_ci struct nfp_fl_ct_map_entry *ct_map_ent) 206562306a36Sopenharmony_ci{ 206662306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *ct_entry = ct_map_ent->ct_entry; 206762306a36Sopenharmony_ci struct nfp_fl_nft_tc_merge *nft_merge, *nft_m_tmp; 206862306a36Sopenharmony_ci struct nfp_fl_ct_tc_merge *tc_merge, *tc_m_tmp; 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci u64 pkts = 0, bytes = 0, used = 0; 207162306a36Sopenharmony_ci u64 m_pkts, m_bytes, m_used; 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci spin_lock_bh(&ct_entry->zt->priv->stats_lock); 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci if (ct_entry->type == CT_TYPE_PRE_CT) { 207662306a36Sopenharmony_ci /* Iterate tc_merge entries associated with this flow */ 207762306a36Sopenharmony_ci list_for_each_entry_safe(tc_merge, tc_m_tmp, &ct_entry->children, 207862306a36Sopenharmony_ci pre_ct_list) { 207962306a36Sopenharmony_ci m_pkts = 0; 208062306a36Sopenharmony_ci m_bytes = 0; 208162306a36Sopenharmony_ci m_used = 0; 208262306a36Sopenharmony_ci /* Iterate nft_merge entries associated with this tc_merge flow */ 208362306a36Sopenharmony_ci list_for_each_entry_safe(nft_merge, nft_m_tmp, &tc_merge->children, 208462306a36Sopenharmony_ci tc_merge_list) { 208562306a36Sopenharmony_ci nfp_fl_ct_sub_stats(nft_merge, CT_TYPE_PRE_CT, 208662306a36Sopenharmony_ci &m_pkts, &m_bytes, &m_used); 208762306a36Sopenharmony_ci } 208862306a36Sopenharmony_ci pkts += m_pkts; 208962306a36Sopenharmony_ci bytes += m_bytes; 209062306a36Sopenharmony_ci used = max_t(u64, used, m_used); 209162306a36Sopenharmony_ci /* Update post_ct partner */ 209262306a36Sopenharmony_ci flow_stats_update(&tc_merge->post_ct_parent->stats, 209362306a36Sopenharmony_ci m_bytes, m_pkts, 0, m_used, 209462306a36Sopenharmony_ci FLOW_ACTION_HW_STATS_DELAYED); 209562306a36Sopenharmony_ci } 209662306a36Sopenharmony_ci } else if (ct_entry->type == CT_TYPE_POST_CT) { 209762306a36Sopenharmony_ci /* Iterate tc_merge entries associated with this flow */ 209862306a36Sopenharmony_ci list_for_each_entry_safe(tc_merge, tc_m_tmp, &ct_entry->children, 209962306a36Sopenharmony_ci post_ct_list) { 210062306a36Sopenharmony_ci m_pkts = 0; 210162306a36Sopenharmony_ci m_bytes = 0; 210262306a36Sopenharmony_ci m_used = 0; 210362306a36Sopenharmony_ci /* Iterate nft_merge entries associated with this tc_merge flow */ 210462306a36Sopenharmony_ci list_for_each_entry_safe(nft_merge, nft_m_tmp, &tc_merge->children, 210562306a36Sopenharmony_ci tc_merge_list) { 210662306a36Sopenharmony_ci nfp_fl_ct_sub_stats(nft_merge, CT_TYPE_POST_CT, 210762306a36Sopenharmony_ci &m_pkts, &m_bytes, &m_used); 210862306a36Sopenharmony_ci } 210962306a36Sopenharmony_ci pkts += m_pkts; 211062306a36Sopenharmony_ci bytes += m_bytes; 211162306a36Sopenharmony_ci used = max_t(u64, used, m_used); 211262306a36Sopenharmony_ci /* Update pre_ct partner */ 211362306a36Sopenharmony_ci flow_stats_update(&tc_merge->pre_ct_parent->stats, 211462306a36Sopenharmony_ci m_bytes, m_pkts, 0, m_used, 211562306a36Sopenharmony_ci FLOW_ACTION_HW_STATS_DELAYED); 211662306a36Sopenharmony_ci } 211762306a36Sopenharmony_ci } else { 211862306a36Sopenharmony_ci /* Iterate nft_merge entries associated with this nft flow */ 211962306a36Sopenharmony_ci list_for_each_entry_safe(nft_merge, nft_m_tmp, &ct_entry->children, 212062306a36Sopenharmony_ci nft_flow_list) { 212162306a36Sopenharmony_ci nfp_fl_ct_sub_stats(nft_merge, CT_TYPE_NFT, 212262306a36Sopenharmony_ci &pkts, &bytes, &used); 212362306a36Sopenharmony_ci } 212462306a36Sopenharmony_ci } 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci /* Add stats from this request to stats potentially cached by 212762306a36Sopenharmony_ci * previous requests. 212862306a36Sopenharmony_ci */ 212962306a36Sopenharmony_ci flow_stats_update(&ct_entry->stats, bytes, pkts, 0, used, 213062306a36Sopenharmony_ci FLOW_ACTION_HW_STATS_DELAYED); 213162306a36Sopenharmony_ci /* Finally update the flow stats from the original stats request */ 213262306a36Sopenharmony_ci flow_stats_update(&flow->stats, ct_entry->stats.bytes, 213362306a36Sopenharmony_ci ct_entry->stats.pkts, 0, 213462306a36Sopenharmony_ci ct_entry->stats.lastused, 213562306a36Sopenharmony_ci FLOW_ACTION_HW_STATS_DELAYED); 213662306a36Sopenharmony_ci /* Stats has been synced to original flow, can now clear 213762306a36Sopenharmony_ci * the cache. 213862306a36Sopenharmony_ci */ 213962306a36Sopenharmony_ci ct_entry->stats.pkts = 0; 214062306a36Sopenharmony_ci ct_entry->stats.bytes = 0; 214162306a36Sopenharmony_ci spin_unlock_bh(&ct_entry->zt->priv->stats_lock); 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci return 0; 214462306a36Sopenharmony_ci} 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_cistatic bool 214762306a36Sopenharmony_cinfp_fl_ct_offload_nft_supported(struct flow_cls_offload *flow) 214862306a36Sopenharmony_ci{ 214962306a36Sopenharmony_ci struct flow_rule *flow_rule = flow->rule; 215062306a36Sopenharmony_ci struct flow_action *flow_action = 215162306a36Sopenharmony_ci &flow_rule->action; 215262306a36Sopenharmony_ci struct flow_action_entry *act; 215362306a36Sopenharmony_ci int i; 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci flow_action_for_each(i, act, flow_action) { 215662306a36Sopenharmony_ci if (act->id == FLOW_ACTION_CT_METADATA) { 215762306a36Sopenharmony_ci enum ip_conntrack_info ctinfo = 215862306a36Sopenharmony_ci act->ct_metadata.cookie & NFCT_INFOMASK; 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci return ctinfo != IP_CT_NEW; 216162306a36Sopenharmony_ci } 216262306a36Sopenharmony_ci } 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci return false; 216562306a36Sopenharmony_ci} 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_cistatic int 216862306a36Sopenharmony_cinfp_fl_ct_offload_nft_flow(struct nfp_fl_ct_zone_entry *zt, struct flow_cls_offload *flow) 216962306a36Sopenharmony_ci{ 217062306a36Sopenharmony_ci struct nfp_fl_ct_map_entry *ct_map_ent; 217162306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *ct_entry; 217262306a36Sopenharmony_ci struct netlink_ext_ack *extack = NULL; 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci extack = flow->common.extack; 217562306a36Sopenharmony_ci switch (flow->command) { 217662306a36Sopenharmony_ci case FLOW_CLS_REPLACE: 217762306a36Sopenharmony_ci if (!nfp_fl_ct_offload_nft_supported(flow)) 217862306a36Sopenharmony_ci return -EOPNOTSUPP; 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci /* Netfilter can request offload multiple times for the same 218162306a36Sopenharmony_ci * flow - protect against adding duplicates. 218262306a36Sopenharmony_ci */ 218362306a36Sopenharmony_ci ct_map_ent = rhashtable_lookup_fast(&zt->priv->ct_map_table, &flow->cookie, 218462306a36Sopenharmony_ci nfp_ct_map_params); 218562306a36Sopenharmony_ci if (!ct_map_ent) { 218662306a36Sopenharmony_ci ct_entry = nfp_fl_ct_add_flow(zt, NULL, flow, true, extack); 218762306a36Sopenharmony_ci if (IS_ERR(ct_entry)) 218862306a36Sopenharmony_ci return PTR_ERR(ct_entry); 218962306a36Sopenharmony_ci ct_entry->type = CT_TYPE_NFT; 219062306a36Sopenharmony_ci list_add(&ct_entry->list_node, &zt->nft_flows_list); 219162306a36Sopenharmony_ci zt->nft_flows_count++; 219262306a36Sopenharmony_ci nfp_ct_merge_nft_with_tc(ct_entry, zt); 219362306a36Sopenharmony_ci } 219462306a36Sopenharmony_ci return 0; 219562306a36Sopenharmony_ci case FLOW_CLS_DESTROY: 219662306a36Sopenharmony_ci ct_map_ent = rhashtable_lookup_fast(&zt->priv->ct_map_table, &flow->cookie, 219762306a36Sopenharmony_ci nfp_ct_map_params); 219862306a36Sopenharmony_ci return nfp_fl_ct_del_flow(ct_map_ent); 219962306a36Sopenharmony_ci case FLOW_CLS_STATS: 220062306a36Sopenharmony_ci ct_map_ent = rhashtable_lookup_fast(&zt->priv->ct_map_table, &flow->cookie, 220162306a36Sopenharmony_ci nfp_ct_map_params); 220262306a36Sopenharmony_ci if (ct_map_ent) 220362306a36Sopenharmony_ci return nfp_fl_ct_stats(flow, ct_map_ent); 220462306a36Sopenharmony_ci break; 220562306a36Sopenharmony_ci default: 220662306a36Sopenharmony_ci break; 220762306a36Sopenharmony_ci } 220862306a36Sopenharmony_ci return -EINVAL; 220962306a36Sopenharmony_ci} 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ciint nfp_fl_ct_handle_nft_flow(enum tc_setup_type type, void *type_data, void *cb_priv) 221262306a36Sopenharmony_ci{ 221362306a36Sopenharmony_ci struct flow_cls_offload *flow = type_data; 221462306a36Sopenharmony_ci struct nfp_fl_ct_zone_entry *zt = cb_priv; 221562306a36Sopenharmony_ci int err = -EOPNOTSUPP; 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ci switch (type) { 221862306a36Sopenharmony_ci case TC_SETUP_CLSFLOWER: 221962306a36Sopenharmony_ci while (!mutex_trylock(&zt->priv->nfp_fl_lock)) { 222062306a36Sopenharmony_ci if (!zt->nft) /* avoid deadlock */ 222162306a36Sopenharmony_ci return err; 222262306a36Sopenharmony_ci msleep(20); 222362306a36Sopenharmony_ci } 222462306a36Sopenharmony_ci err = nfp_fl_ct_offload_nft_flow(zt, flow); 222562306a36Sopenharmony_ci mutex_unlock(&zt->priv->nfp_fl_lock); 222662306a36Sopenharmony_ci break; 222762306a36Sopenharmony_ci default: 222862306a36Sopenharmony_ci return -EOPNOTSUPP; 222962306a36Sopenharmony_ci } 223062306a36Sopenharmony_ci return err; 223162306a36Sopenharmony_ci} 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_cistatic void 223462306a36Sopenharmony_cinfp_fl_ct_clean_nft_entries(struct nfp_fl_ct_zone_entry *zt) 223562306a36Sopenharmony_ci{ 223662306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *nft_entry, *ct_tmp; 223762306a36Sopenharmony_ci struct nfp_fl_ct_map_entry *ct_map_ent; 223862306a36Sopenharmony_ci 223962306a36Sopenharmony_ci list_for_each_entry_safe(nft_entry, ct_tmp, &zt->nft_flows_list, 224062306a36Sopenharmony_ci list_node) { 224162306a36Sopenharmony_ci ct_map_ent = rhashtable_lookup_fast(&zt->priv->ct_map_table, 224262306a36Sopenharmony_ci &nft_entry->cookie, 224362306a36Sopenharmony_ci nfp_ct_map_params); 224462306a36Sopenharmony_ci nfp_fl_ct_del_flow(ct_map_ent); 224562306a36Sopenharmony_ci } 224662306a36Sopenharmony_ci} 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ciint nfp_fl_ct_del_flow(struct nfp_fl_ct_map_entry *ct_map_ent) 224962306a36Sopenharmony_ci{ 225062306a36Sopenharmony_ci struct nfp_fl_ct_flow_entry *ct_entry; 225162306a36Sopenharmony_ci struct nfp_fl_ct_zone_entry *zt; 225262306a36Sopenharmony_ci struct rhashtable *m_table; 225362306a36Sopenharmony_ci struct nf_flowtable *nft; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci if (!ct_map_ent) 225662306a36Sopenharmony_ci return -ENOENT; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci zt = ct_map_ent->ct_entry->zt; 225962306a36Sopenharmony_ci ct_entry = ct_map_ent->ct_entry; 226062306a36Sopenharmony_ci m_table = &zt->priv->ct_map_table; 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci switch (ct_entry->type) { 226362306a36Sopenharmony_ci case CT_TYPE_PRE_CT: 226462306a36Sopenharmony_ci zt->pre_ct_count--; 226562306a36Sopenharmony_ci if (ct_map_ent->cookie > 0) 226662306a36Sopenharmony_ci rhashtable_remove_fast(m_table, &ct_map_ent->hash_node, 226762306a36Sopenharmony_ci nfp_ct_map_params); 226862306a36Sopenharmony_ci nfp_fl_ct_clean_flow_entry(ct_entry); 226962306a36Sopenharmony_ci if (ct_map_ent->cookie > 0) 227062306a36Sopenharmony_ci kfree(ct_map_ent); 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci if (!zt->pre_ct_count && zt->nft) { 227362306a36Sopenharmony_ci nft = zt->nft; 227462306a36Sopenharmony_ci zt->nft = NULL; /* avoid deadlock */ 227562306a36Sopenharmony_ci nf_flow_table_offload_del_cb(nft, 227662306a36Sopenharmony_ci nfp_fl_ct_handle_nft_flow, 227762306a36Sopenharmony_ci zt); 227862306a36Sopenharmony_ci nfp_fl_ct_clean_nft_entries(zt); 227962306a36Sopenharmony_ci } 228062306a36Sopenharmony_ci break; 228162306a36Sopenharmony_ci case CT_TYPE_POST_CT: 228262306a36Sopenharmony_ci zt->post_ct_count--; 228362306a36Sopenharmony_ci rhashtable_remove_fast(m_table, &ct_map_ent->hash_node, 228462306a36Sopenharmony_ci nfp_ct_map_params); 228562306a36Sopenharmony_ci nfp_fl_ct_clean_flow_entry(ct_entry); 228662306a36Sopenharmony_ci kfree(ct_map_ent); 228762306a36Sopenharmony_ci break; 228862306a36Sopenharmony_ci case CT_TYPE_NFT: 228962306a36Sopenharmony_ci zt->nft_flows_count--; 229062306a36Sopenharmony_ci rhashtable_remove_fast(m_table, &ct_map_ent->hash_node, 229162306a36Sopenharmony_ci nfp_ct_map_params); 229262306a36Sopenharmony_ci nfp_fl_ct_clean_flow_entry(ct_map_ent->ct_entry); 229362306a36Sopenharmony_ci kfree(ct_map_ent); 229462306a36Sopenharmony_ci break; 229562306a36Sopenharmony_ci default: 229662306a36Sopenharmony_ci break; 229762306a36Sopenharmony_ci } 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci return 0; 230062306a36Sopenharmony_ci} 2301