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