xref: /openbmc/linux/drivers/net/ethernet/netronome/nfp/flower/conntrack.c (revision c595db6d7c8bcf87ef42204391fa890e5950e566)
1c8b034fbSLouis Peens // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2c8b034fbSLouis Peens /* Copyright (C) 2021 Corigine, Inc. */
3c8b034fbSLouis Peens 
45cee92c6SHui Zhou #include <net/tc_act/tc_csum.h>
55cee92c6SHui Zhou #include <net/tc_act/tc_ct.h>
65cee92c6SHui Zhou 
7c8b034fbSLouis Peens #include "conntrack.h"
8453cdc30SLouis Peens #include "../nfp_port.h"
9c8b034fbSLouis Peens 
10f7ae12e2SLouis Peens const struct rhashtable_params nfp_tc_ct_merge_params = {
11f7ae12e2SLouis Peens 	.head_offset		= offsetof(struct nfp_fl_ct_tc_merge,
12f7ae12e2SLouis Peens 					   hash_node),
13f7ae12e2SLouis Peens 	.key_len		= sizeof(unsigned long) * 2,
14f7ae12e2SLouis Peens 	.key_offset		= offsetof(struct nfp_fl_ct_tc_merge, cookie),
15f7ae12e2SLouis Peens 	.automatic_shrinking	= true,
16f7ae12e2SLouis Peens };
17f7ae12e2SLouis Peens 
18b5e30c61SLouis Peens const struct rhashtable_params nfp_nft_ct_merge_params = {
19b5e30c61SLouis Peens 	.head_offset		= offsetof(struct nfp_fl_nft_tc_merge,
20b5e30c61SLouis Peens 					   hash_node),
21b5e30c61SLouis Peens 	.key_len		= sizeof(unsigned long) * 3,
22b5e30c61SLouis Peens 	.key_offset		= offsetof(struct nfp_fl_nft_tc_merge, cookie),
23b5e30c61SLouis Peens 	.automatic_shrinking	= true,
24b5e30c61SLouis Peens };
25b5e30c61SLouis Peens 
265e5f0816SLouis Peens static struct flow_action_entry *get_flow_act(struct flow_rule *rule,
275e5f0816SLouis Peens 					      enum flow_action_id act_id);
285e5f0816SLouis Peens 
29bd0fe7f9SLouis Peens /**
30bd0fe7f9SLouis Peens  * get_hashentry() - Wrapper around hashtable lookup.
31bd0fe7f9SLouis Peens  * @ht:		hashtable where entry could be found
32bd0fe7f9SLouis Peens  * @key:	key to lookup
33bd0fe7f9SLouis Peens  * @params:	hashtable params
34bd0fe7f9SLouis Peens  * @size:	size of entry to allocate if not in table
35bd0fe7f9SLouis Peens  *
36bd0fe7f9SLouis Peens  * Returns an entry from a hashtable. If entry does not exist
37bd0fe7f9SLouis Peens  * yet allocate the memory for it and return the new entry.
38bd0fe7f9SLouis Peens  */
get_hashentry(struct rhashtable * ht,void * key,const struct rhashtable_params params,size_t size)39bd0fe7f9SLouis Peens static void *get_hashentry(struct rhashtable *ht, void *key,
40bd0fe7f9SLouis Peens 			   const struct rhashtable_params params, size_t size)
41bd0fe7f9SLouis Peens {
42bd0fe7f9SLouis Peens 	void *result;
43bd0fe7f9SLouis Peens 
44bd0fe7f9SLouis Peens 	result = rhashtable_lookup_fast(ht, key, params);
45bd0fe7f9SLouis Peens 
46bd0fe7f9SLouis Peens 	if (result)
47bd0fe7f9SLouis Peens 		return result;
48bd0fe7f9SLouis Peens 
49bd0fe7f9SLouis Peens 	result = kzalloc(size, GFP_KERNEL);
50bd0fe7f9SLouis Peens 	if (!result)
51bd0fe7f9SLouis Peens 		return ERR_PTR(-ENOMEM);
52bd0fe7f9SLouis Peens 
53bd0fe7f9SLouis Peens 	return result;
54bd0fe7f9SLouis Peens }
55bd0fe7f9SLouis Peens 
is_pre_ct_flow(struct flow_cls_offload * flow)56c8b034fbSLouis Peens bool is_pre_ct_flow(struct flow_cls_offload *flow)
57c8b034fbSLouis Peens {
58cee7b339SWentao Jia 	struct flow_rule *rule = flow_cls_offload_flow_rule(flow);
59cee7b339SWentao Jia 	struct flow_dissector *dissector = rule->match.dissector;
60c8b034fbSLouis Peens 	struct flow_action_entry *act;
61cee7b339SWentao Jia 	struct flow_match_ct ct;
62c8b034fbSLouis Peens 	int i;
63c8b034fbSLouis Peens 
642b3082c6SRatheesh Kannoth 	if (dissector->used_keys & BIT_ULL(FLOW_DISSECTOR_KEY_CT)) {
65cee7b339SWentao Jia 		flow_rule_match_ct(rule, &ct);
66cee7b339SWentao Jia 		if (ct.key->ct_state)
67cee7b339SWentao Jia 			return false;
68cee7b339SWentao Jia 	}
69cee7b339SWentao Jia 
70cee7b339SWentao Jia 	if (flow->common.chain_index)
71cee7b339SWentao Jia 		return false;
72cee7b339SWentao Jia 
73c8b034fbSLouis Peens 	flow_action_for_each(i, act, &flow->rule->action) {
745cee92c6SHui Zhou 		if (act->id == FLOW_ACTION_CT) {
755cee92c6SHui Zhou 			/* The pre_ct rule only have the ct or ct nat action, cannot
765cee92c6SHui Zhou 			 * contains other ct action e.g ct commit and so on.
775cee92c6SHui Zhou 			 */
785cee92c6SHui Zhou 			if ((!act->ct.action || act->ct.action == TCA_CT_ACT_NAT))
79c8b034fbSLouis Peens 				return true;
805cee92c6SHui Zhou 			else
815cee92c6SHui Zhou 				return false;
82c8b034fbSLouis Peens 		}
835cee92c6SHui Zhou 	}
845cee92c6SHui Zhou 
85c8b034fbSLouis Peens 	return false;
86c8b034fbSLouis Peens }
87c8b034fbSLouis Peens 
is_post_ct_flow(struct flow_cls_offload * flow)88c8b034fbSLouis Peens bool is_post_ct_flow(struct flow_cls_offload *flow)
89c8b034fbSLouis Peens {
90c8b034fbSLouis Peens 	struct flow_rule *rule = flow_cls_offload_flow_rule(flow);
91c8b034fbSLouis Peens 	struct flow_dissector *dissector = rule->match.dissector;
925cee92c6SHui Zhou 	struct flow_action_entry *act;
935cee92c6SHui Zhou 	bool exist_ct_clear = false;
94c8b034fbSLouis Peens 	struct flow_match_ct ct;
955cee92c6SHui Zhou 	int i;
965cee92c6SHui Zhou 
972b3082c6SRatheesh Kannoth 	if (dissector->used_keys & BIT_ULL(FLOW_DISSECTOR_KEY_CT)) {
980b8d953cSWentao Jia 		flow_rule_match_ct(rule, &ct);
990b8d953cSWentao Jia 		if (ct.key->ct_state & TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED)
1000b8d953cSWentao Jia 			return true;
1010b8d953cSWentao Jia 	} else {
1025cee92c6SHui Zhou 		/* post ct entry cannot contains any ct action except ct_clear. */
1035cee92c6SHui Zhou 		flow_action_for_each(i, act, &flow->rule->action) {
1045cee92c6SHui Zhou 			if (act->id == FLOW_ACTION_CT) {
1055cee92c6SHui Zhou 				/* ignore ct clear action. */
1065cee92c6SHui Zhou 				if (act->ct.action == TCA_CT_ACT_CLEAR) {
1075cee92c6SHui Zhou 					exist_ct_clear = true;
1085cee92c6SHui Zhou 					continue;
1095cee92c6SHui Zhou 				}
1105cee92c6SHui Zhou 
1115cee92c6SHui Zhou 				return false;
1125cee92c6SHui Zhou 			}
1135cee92c6SHui Zhou 		}
1145cee92c6SHui Zhou 		/* when do nat with ct, the post ct entry ignore the ct status,
1155cee92c6SHui Zhou 		 * will match the nat field(sip/dip) instead. In this situation,
1165cee92c6SHui Zhou 		 * the flow chain index is not zero and contains ct clear action.
1175cee92c6SHui Zhou 		 */
1185cee92c6SHui Zhou 		if (flow->common.chain_index && exist_ct_clear)
1195cee92c6SHui Zhou 			return true;
120c8b034fbSLouis Peens 	}
1215cee92c6SHui Zhou 
122c8b034fbSLouis Peens 	return false;
123c8b034fbSLouis Peens }
124c8b034fbSLouis Peens 
125e43d940fSYinjun Zhang /**
126e43d940fSYinjun Zhang  * get_mangled_key() - Mangle the key if mangle act exists
127e43d940fSYinjun Zhang  * @rule:	rule that carries the actions
128e43d940fSYinjun Zhang  * @buf:	pointer to key to be mangled
129e43d940fSYinjun Zhang  * @offset:	used to adjust mangled offset in L2/L3/L4 header
130e43d940fSYinjun Zhang  * @key_sz:	key size
131e43d940fSYinjun Zhang  * @htype:	mangling type
132e43d940fSYinjun Zhang  *
133e43d940fSYinjun Zhang  * Returns buf where the mangled key stores.
134e43d940fSYinjun Zhang  */
get_mangled_key(struct flow_rule * rule,void * buf,u32 offset,size_t key_sz,enum flow_action_mangle_base htype)135e43d940fSYinjun Zhang static void *get_mangled_key(struct flow_rule *rule, void *buf,
136e43d940fSYinjun Zhang 			     u32 offset, size_t key_sz,
137e43d940fSYinjun Zhang 			     enum flow_action_mangle_base htype)
138e43d940fSYinjun Zhang {
139e43d940fSYinjun Zhang 	struct flow_action_entry *act;
140e43d940fSYinjun Zhang 	u32 *val = (u32 *)buf;
141e43d940fSYinjun Zhang 	u32 off, msk, key;
142e43d940fSYinjun Zhang 	int i;
143e43d940fSYinjun Zhang 
144e43d940fSYinjun Zhang 	flow_action_for_each(i, act, &rule->action) {
145e43d940fSYinjun Zhang 		if (act->id == FLOW_ACTION_MANGLE &&
146e43d940fSYinjun Zhang 		    act->mangle.htype == htype) {
147e43d940fSYinjun Zhang 			off = act->mangle.offset - offset;
148e43d940fSYinjun Zhang 			msk = act->mangle.mask;
149e43d940fSYinjun Zhang 			key = act->mangle.val;
150e43d940fSYinjun Zhang 
151e43d940fSYinjun Zhang 			/* Mangling is supposed to be u32 aligned */
152e43d940fSYinjun Zhang 			if (off % 4 || off >= key_sz)
153e43d940fSYinjun Zhang 				continue;
154e43d940fSYinjun Zhang 
155e43d940fSYinjun Zhang 			val[off >> 2] &= msk;
156e43d940fSYinjun Zhang 			val[off >> 2] |= key;
157e43d940fSYinjun Zhang 		}
158e43d940fSYinjun Zhang 	}
159e43d940fSYinjun Zhang 
160e43d940fSYinjun Zhang 	return buf;
161e43d940fSYinjun Zhang }
162e43d940fSYinjun Zhang 
163e43d940fSYinjun Zhang /* Only tos and ttl are involved in flow_match_ip structure, which
164e43d940fSYinjun Zhang  * doesn't conform to the layout of ip/ipv6 header definition. So
165e43d940fSYinjun Zhang  * they need particular process here: fill them into the ip/ipv6
166e43d940fSYinjun Zhang  * header, so that mangling actions can work directly.
167e43d940fSYinjun Zhang  */
168e43d940fSYinjun Zhang #define NFP_IPV4_TOS_MASK	GENMASK(23, 16)
169e43d940fSYinjun Zhang #define NFP_IPV4_TTL_MASK	GENMASK(31, 24)
170e43d940fSYinjun Zhang #define NFP_IPV6_TCLASS_MASK	GENMASK(27, 20)
171e43d940fSYinjun Zhang #define NFP_IPV6_HLIMIT_MASK	GENMASK(7, 0)
get_mangled_tos_ttl(struct flow_rule * rule,void * buf,bool is_v6)172e43d940fSYinjun Zhang static void *get_mangled_tos_ttl(struct flow_rule *rule, void *buf,
173e43d940fSYinjun Zhang 				 bool is_v6)
174e43d940fSYinjun Zhang {
175e43d940fSYinjun Zhang 	struct flow_match_ip match;
176e43d940fSYinjun Zhang 	/* IPv4's ttl field is in third dword. */
177e43d940fSYinjun Zhang 	__be32 ip_hdr[3];
178e43d940fSYinjun Zhang 	u32 tmp, hdr_len;
179e43d940fSYinjun Zhang 
180e43d940fSYinjun Zhang 	flow_rule_match_ip(rule, &match);
181e43d940fSYinjun Zhang 
182e43d940fSYinjun Zhang 	if (is_v6) {
183e43d940fSYinjun Zhang 		tmp = FIELD_PREP(NFP_IPV6_TCLASS_MASK, match.key->tos);
184e43d940fSYinjun Zhang 		ip_hdr[0] = cpu_to_be32(tmp);
185e43d940fSYinjun Zhang 		tmp = FIELD_PREP(NFP_IPV6_HLIMIT_MASK, match.key->ttl);
186e43d940fSYinjun Zhang 		ip_hdr[1] = cpu_to_be32(tmp);
187e43d940fSYinjun Zhang 		hdr_len = 2 * sizeof(__be32);
188e43d940fSYinjun Zhang 	} else {
189e43d940fSYinjun Zhang 		tmp = FIELD_PREP(NFP_IPV4_TOS_MASK, match.key->tos);
190e43d940fSYinjun Zhang 		ip_hdr[0] = cpu_to_be32(tmp);
191e43d940fSYinjun Zhang 		tmp = FIELD_PREP(NFP_IPV4_TTL_MASK, match.key->ttl);
192e43d940fSYinjun Zhang 		ip_hdr[2] = cpu_to_be32(tmp);
193e43d940fSYinjun Zhang 		hdr_len = 3 * sizeof(__be32);
194e43d940fSYinjun Zhang 	}
195e43d940fSYinjun Zhang 
196e43d940fSYinjun Zhang 	get_mangled_key(rule, ip_hdr, 0, hdr_len,
197e43d940fSYinjun Zhang 			is_v6 ? FLOW_ACT_MANGLE_HDR_TYPE_IP6 :
198e43d940fSYinjun Zhang 				FLOW_ACT_MANGLE_HDR_TYPE_IP4);
199e43d940fSYinjun Zhang 
200e43d940fSYinjun Zhang 	match.key = buf;
201e43d940fSYinjun Zhang 
202e43d940fSYinjun Zhang 	if (is_v6) {
203e43d940fSYinjun Zhang 		tmp = be32_to_cpu(ip_hdr[0]);
204e43d940fSYinjun Zhang 		match.key->tos = FIELD_GET(NFP_IPV6_TCLASS_MASK, tmp);
205e43d940fSYinjun Zhang 		tmp = be32_to_cpu(ip_hdr[1]);
206e43d940fSYinjun Zhang 		match.key->ttl = FIELD_GET(NFP_IPV6_HLIMIT_MASK, tmp);
207e43d940fSYinjun Zhang 	} else {
208e43d940fSYinjun Zhang 		tmp = be32_to_cpu(ip_hdr[0]);
209e43d940fSYinjun Zhang 		match.key->tos = FIELD_GET(NFP_IPV4_TOS_MASK, tmp);
210e43d940fSYinjun Zhang 		tmp = be32_to_cpu(ip_hdr[2]);
211e43d940fSYinjun Zhang 		match.key->ttl = FIELD_GET(NFP_IPV4_TTL_MASK, tmp);
212e43d940fSYinjun Zhang 	}
213e43d940fSYinjun Zhang 
214e43d940fSYinjun Zhang 	return buf;
215e43d940fSYinjun Zhang }
216e43d940fSYinjun Zhang 
2175cee92c6SHui Zhou /* Note entry1 and entry2 are not swappable. only skip ip and
2185cee92c6SHui Zhou  * tport merge check for pre_ct and post_ct when pre_ct do nat.
2195cee92c6SHui Zhou  */
nfp_ct_merge_check_cannot_skip(struct nfp_fl_ct_flow_entry * entry1,struct nfp_fl_ct_flow_entry * entry2)2205cee92c6SHui Zhou static bool nfp_ct_merge_check_cannot_skip(struct nfp_fl_ct_flow_entry *entry1,
2215cee92c6SHui Zhou 					   struct nfp_fl_ct_flow_entry *entry2)
2225cee92c6SHui Zhou {
2235cee92c6SHui Zhou 	/* only pre_ct have NFP_FL_ACTION_DO_NAT flag. */
2245cee92c6SHui Zhou 	if ((entry1->flags & NFP_FL_ACTION_DO_NAT) &&
2255cee92c6SHui Zhou 	    entry2->type == CT_TYPE_POST_CT)
2265cee92c6SHui Zhou 		return false;
2275cee92c6SHui Zhou 
2285cee92c6SHui Zhou 	return true;
2295cee92c6SHui Zhou }
2305cee92c6SHui Zhou 
231e43d940fSYinjun Zhang /* Note entry1 and entry2 are not swappable, entry1 should be
232e43d940fSYinjun Zhang  * the former flow whose mangle action need be taken into account
233e43d940fSYinjun Zhang  * if existed, and entry2 should be the latter flow whose action
234e43d940fSYinjun Zhang  * we don't care.
235e43d940fSYinjun Zhang  */
nfp_ct_merge_check(struct nfp_fl_ct_flow_entry * entry1,struct nfp_fl_ct_flow_entry * entry2)2363c863c30SLouis Peens static int nfp_ct_merge_check(struct nfp_fl_ct_flow_entry *entry1,
2373c863c30SLouis Peens 			      struct nfp_fl_ct_flow_entry *entry2)
2383c863c30SLouis Peens {
2392b3082c6SRatheesh Kannoth 	unsigned long long ovlp_keys;
240e43d940fSYinjun Zhang 	bool out, is_v6 = false;
241e43d940fSYinjun Zhang 	u8 ip_proto = 0;
2422b3082c6SRatheesh Kannoth 	ovlp_keys = entry1->rule->match.dissector->used_keys &
2432b3082c6SRatheesh Kannoth 			entry2->rule->match.dissector->used_keys;
244e43d940fSYinjun Zhang 	/* Temporary buffer for mangling keys, 64 is enough to cover max
245e43d940fSYinjun Zhang 	 * struct size of key in various fields that may be mangled.
2469bacb93bSWalter Heymans 	 * Supported fields to mangle:
247e43d940fSYinjun Zhang 	 * mac_src/mac_dst(struct flow_match_eth_addrs, 12B)
248e43d940fSYinjun Zhang 	 * nw_tos/nw_ttl(struct flow_match_ip, 2B)
249e43d940fSYinjun Zhang 	 * nw_src/nw_dst(struct flow_match_ipv4/6_addrs, 32B)
250e43d940fSYinjun Zhang 	 * tp_src/tp_dst(struct flow_match_ports, 4B)
251e43d940fSYinjun Zhang 	 */
252e43d940fSYinjun Zhang 	char buf[64];
253c698e2adSLouis Peens 
2547195464cSYinjun Zhang 	if (entry1->netdev && entry2->netdev &&
2557195464cSYinjun Zhang 	    entry1->netdev != entry2->netdev)
2567195464cSYinjun Zhang 		return -EINVAL;
2577195464cSYinjun Zhang 
2589bacb93bSWalter Heymans 	/* Check the overlapped fields one by one, the unmasked part
259c698e2adSLouis Peens 	 * should not conflict with each other.
260c698e2adSLouis Peens 	 */
2612b3082c6SRatheesh Kannoth 	if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL)) {
262c698e2adSLouis Peens 		struct flow_match_control match1, match2;
263c698e2adSLouis Peens 
264c698e2adSLouis Peens 		flow_rule_match_control(entry1->rule, &match1);
265c698e2adSLouis Peens 		flow_rule_match_control(entry2->rule, &match2);
266c698e2adSLouis Peens 		COMPARE_UNMASKED_FIELDS(match1, match2, &out);
267c698e2adSLouis Peens 		if (out)
268c698e2adSLouis Peens 			goto check_failed;
269c698e2adSLouis Peens 	}
270c698e2adSLouis Peens 
2712b3082c6SRatheesh Kannoth 	if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_BASIC)) {
272c698e2adSLouis Peens 		struct flow_match_basic match1, match2;
273c698e2adSLouis Peens 
274c698e2adSLouis Peens 		flow_rule_match_basic(entry1->rule, &match1);
275c698e2adSLouis Peens 		flow_rule_match_basic(entry2->rule, &match2);
276e43d940fSYinjun Zhang 
277e43d940fSYinjun Zhang 		/* n_proto field is a must in ct-related flows,
278e43d940fSYinjun Zhang 		 * it should be either ipv4 or ipv6.
279e43d940fSYinjun Zhang 		 */
280e43d940fSYinjun Zhang 		is_v6 = match1.key->n_proto == htons(ETH_P_IPV6);
281e43d940fSYinjun Zhang 		/* ip_proto field is a must when port field is cared */
282e43d940fSYinjun Zhang 		ip_proto = match1.key->ip_proto;
283e43d940fSYinjun Zhang 
284c698e2adSLouis Peens 		COMPARE_UNMASKED_FIELDS(match1, match2, &out);
285c698e2adSLouis Peens 		if (out)
286c698e2adSLouis Peens 			goto check_failed;
287c698e2adSLouis Peens 	}
288c698e2adSLouis Peens 
2895cee92c6SHui Zhou 	/* if pre ct entry do nat, the nat ip exists in nft entry,
2905cee92c6SHui Zhou 	 * will be do merge check when do nft and post ct merge,
2915cee92c6SHui Zhou 	 * so skip this ip merge check here.
2925cee92c6SHui Zhou 	 */
2932b3082c6SRatheesh Kannoth 	if ((ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS)) &&
2945cee92c6SHui Zhou 	    nfp_ct_merge_check_cannot_skip(entry1, entry2)) {
295c698e2adSLouis Peens 		struct flow_match_ipv4_addrs match1, match2;
296c698e2adSLouis Peens 
297c698e2adSLouis Peens 		flow_rule_match_ipv4_addrs(entry1->rule, &match1);
298c698e2adSLouis Peens 		flow_rule_match_ipv4_addrs(entry2->rule, &match2);
299e43d940fSYinjun Zhang 
300e43d940fSYinjun Zhang 		memcpy(buf, match1.key, sizeof(*match1.key));
301e43d940fSYinjun Zhang 		match1.key = get_mangled_key(entry1->rule, buf,
302e43d940fSYinjun Zhang 					     offsetof(struct iphdr, saddr),
303e43d940fSYinjun Zhang 					     sizeof(*match1.key),
304e43d940fSYinjun Zhang 					     FLOW_ACT_MANGLE_HDR_TYPE_IP4);
305e43d940fSYinjun Zhang 
306c698e2adSLouis Peens 		COMPARE_UNMASKED_FIELDS(match1, match2, &out);
307c698e2adSLouis Peens 		if (out)
308c698e2adSLouis Peens 			goto check_failed;
309c698e2adSLouis Peens 	}
310c698e2adSLouis Peens 
3115cee92c6SHui Zhou 	/* if pre ct entry do nat, the nat ip exists in nft entry,
3125cee92c6SHui Zhou 	 * will be do merge check when do nft and post ct merge,
3135cee92c6SHui Zhou 	 * so skip this ip merge check here.
3145cee92c6SHui Zhou 	 */
3152b3082c6SRatheesh Kannoth 	if ((ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS)) &&
3165cee92c6SHui Zhou 	    nfp_ct_merge_check_cannot_skip(entry1, entry2)) {
317c698e2adSLouis Peens 		struct flow_match_ipv6_addrs match1, match2;
318c698e2adSLouis Peens 
319c698e2adSLouis Peens 		flow_rule_match_ipv6_addrs(entry1->rule, &match1);
320c698e2adSLouis Peens 		flow_rule_match_ipv6_addrs(entry2->rule, &match2);
321e43d940fSYinjun Zhang 
322e43d940fSYinjun Zhang 		memcpy(buf, match1.key, sizeof(*match1.key));
323e43d940fSYinjun Zhang 		match1.key = get_mangled_key(entry1->rule, buf,
324e43d940fSYinjun Zhang 					     offsetof(struct ipv6hdr, saddr),
325e43d940fSYinjun Zhang 					     sizeof(*match1.key),
326e43d940fSYinjun Zhang 					     FLOW_ACT_MANGLE_HDR_TYPE_IP6);
327e43d940fSYinjun Zhang 
328c698e2adSLouis Peens 		COMPARE_UNMASKED_FIELDS(match1, match2, &out);
329c698e2adSLouis Peens 		if (out)
330c698e2adSLouis Peens 			goto check_failed;
331c698e2adSLouis Peens 	}
332c698e2adSLouis Peens 
3335cee92c6SHui Zhou 	/* if pre ct entry do nat, the nat tport exists in nft entry,
3345cee92c6SHui Zhou 	 * will be do merge check when do nft and post ct merge,
3355cee92c6SHui Zhou 	 * so skip this tport merge check here.
3365cee92c6SHui Zhou 	 */
3372b3082c6SRatheesh Kannoth 	if ((ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_PORTS)) &&
3385cee92c6SHui Zhou 	    nfp_ct_merge_check_cannot_skip(entry1, entry2)) {
339e43d940fSYinjun Zhang 		enum flow_action_mangle_base htype = FLOW_ACT_MANGLE_UNSPEC;
340c698e2adSLouis Peens 		struct flow_match_ports match1, match2;
341c698e2adSLouis Peens 
342c698e2adSLouis Peens 		flow_rule_match_ports(entry1->rule, &match1);
343c698e2adSLouis Peens 		flow_rule_match_ports(entry2->rule, &match2);
344e43d940fSYinjun Zhang 
345e43d940fSYinjun Zhang 		if (ip_proto == IPPROTO_UDP)
346e43d940fSYinjun Zhang 			htype = FLOW_ACT_MANGLE_HDR_TYPE_UDP;
347e43d940fSYinjun Zhang 		else if (ip_proto == IPPROTO_TCP)
348e43d940fSYinjun Zhang 			htype = FLOW_ACT_MANGLE_HDR_TYPE_TCP;
349e43d940fSYinjun Zhang 
350e43d940fSYinjun Zhang 		memcpy(buf, match1.key, sizeof(*match1.key));
351e43d940fSYinjun Zhang 		match1.key = get_mangled_key(entry1->rule, buf, 0,
352e43d940fSYinjun Zhang 					     sizeof(*match1.key), htype);
353e43d940fSYinjun Zhang 
354c698e2adSLouis Peens 		COMPARE_UNMASKED_FIELDS(match1, match2, &out);
355c698e2adSLouis Peens 		if (out)
356c698e2adSLouis Peens 			goto check_failed;
357c698e2adSLouis Peens 	}
358c698e2adSLouis Peens 
3592b3082c6SRatheesh Kannoth 	if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
360c698e2adSLouis Peens 		struct flow_match_eth_addrs match1, match2;
361c698e2adSLouis Peens 
362c698e2adSLouis Peens 		flow_rule_match_eth_addrs(entry1->rule, &match1);
363c698e2adSLouis Peens 		flow_rule_match_eth_addrs(entry2->rule, &match2);
364e43d940fSYinjun Zhang 
365e43d940fSYinjun Zhang 		memcpy(buf, match1.key, sizeof(*match1.key));
366e43d940fSYinjun Zhang 		match1.key = get_mangled_key(entry1->rule, buf, 0,
367e43d940fSYinjun Zhang 					     sizeof(*match1.key),
368e43d940fSYinjun Zhang 					     FLOW_ACT_MANGLE_HDR_TYPE_ETH);
369e43d940fSYinjun Zhang 
370c698e2adSLouis Peens 		COMPARE_UNMASKED_FIELDS(match1, match2, &out);
371c698e2adSLouis Peens 		if (out)
372c698e2adSLouis Peens 			goto check_failed;
373c698e2adSLouis Peens 	}
374c698e2adSLouis Peens 
3752b3082c6SRatheesh Kannoth 	if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_VLAN)) {
376c698e2adSLouis Peens 		struct flow_match_vlan match1, match2;
377c698e2adSLouis Peens 
378c698e2adSLouis Peens 		flow_rule_match_vlan(entry1->rule, &match1);
379c698e2adSLouis Peens 		flow_rule_match_vlan(entry2->rule, &match2);
380c698e2adSLouis Peens 		COMPARE_UNMASKED_FIELDS(match1, match2, &out);
381c698e2adSLouis Peens 		if (out)
382c698e2adSLouis Peens 			goto check_failed;
383c698e2adSLouis Peens 	}
384c698e2adSLouis Peens 
3852b3082c6SRatheesh Kannoth 	if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_MPLS)) {
386c698e2adSLouis Peens 		struct flow_match_mpls match1, match2;
387c698e2adSLouis Peens 
388c698e2adSLouis Peens 		flow_rule_match_mpls(entry1->rule, &match1);
389c698e2adSLouis Peens 		flow_rule_match_mpls(entry2->rule, &match2);
390c698e2adSLouis Peens 		COMPARE_UNMASKED_FIELDS(match1, match2, &out);
391c698e2adSLouis Peens 		if (out)
392c698e2adSLouis Peens 			goto check_failed;
393c698e2adSLouis Peens 	}
394c698e2adSLouis Peens 
3952b3082c6SRatheesh Kannoth 	if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_TCP)) {
396c698e2adSLouis Peens 		struct flow_match_tcp match1, match2;
397c698e2adSLouis Peens 
398c698e2adSLouis Peens 		flow_rule_match_tcp(entry1->rule, &match1);
399c698e2adSLouis Peens 		flow_rule_match_tcp(entry2->rule, &match2);
400c698e2adSLouis Peens 		COMPARE_UNMASKED_FIELDS(match1, match2, &out);
401c698e2adSLouis Peens 		if (out)
402c698e2adSLouis Peens 			goto check_failed;
403c698e2adSLouis Peens 	}
404c698e2adSLouis Peens 
4052b3082c6SRatheesh Kannoth 	if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_IP)) {
406c698e2adSLouis Peens 		struct flow_match_ip match1, match2;
407c698e2adSLouis Peens 
408c698e2adSLouis Peens 		flow_rule_match_ip(entry1->rule, &match1);
409c698e2adSLouis Peens 		flow_rule_match_ip(entry2->rule, &match2);
410e43d940fSYinjun Zhang 
411e43d940fSYinjun Zhang 		match1.key = get_mangled_tos_ttl(entry1->rule, buf, is_v6);
412c698e2adSLouis Peens 		COMPARE_UNMASKED_FIELDS(match1, match2, &out);
413c698e2adSLouis Peens 		if (out)
414c698e2adSLouis Peens 			goto check_failed;
415c698e2adSLouis Peens 	}
416c698e2adSLouis Peens 
4172b3082c6SRatheesh Kannoth 	if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_ENC_KEYID)) {
418c698e2adSLouis Peens 		struct flow_match_enc_keyid match1, match2;
419c698e2adSLouis Peens 
420c698e2adSLouis Peens 		flow_rule_match_enc_keyid(entry1->rule, &match1);
421c698e2adSLouis Peens 		flow_rule_match_enc_keyid(entry2->rule, &match2);
422c698e2adSLouis Peens 		COMPARE_UNMASKED_FIELDS(match1, match2, &out);
423c698e2adSLouis Peens 		if (out)
424c698e2adSLouis Peens 			goto check_failed;
425c698e2adSLouis Peens 	}
426c698e2adSLouis Peens 
4272b3082c6SRatheesh Kannoth 	if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) {
428c698e2adSLouis Peens 		struct flow_match_ipv4_addrs match1, match2;
429c698e2adSLouis Peens 
430c698e2adSLouis Peens 		flow_rule_match_enc_ipv4_addrs(entry1->rule, &match1);
431c698e2adSLouis Peens 		flow_rule_match_enc_ipv4_addrs(entry2->rule, &match2);
432c698e2adSLouis Peens 		COMPARE_UNMASKED_FIELDS(match1, match2, &out);
433c698e2adSLouis Peens 		if (out)
434c698e2adSLouis Peens 			goto check_failed;
435c698e2adSLouis Peens 	}
436c698e2adSLouis Peens 
4372b3082c6SRatheesh Kannoth 	if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS)) {
438c698e2adSLouis Peens 		struct flow_match_ipv6_addrs match1, match2;
439c698e2adSLouis Peens 
440c698e2adSLouis Peens 		flow_rule_match_enc_ipv6_addrs(entry1->rule, &match1);
441c698e2adSLouis Peens 		flow_rule_match_enc_ipv6_addrs(entry2->rule, &match2);
442c698e2adSLouis Peens 		COMPARE_UNMASKED_FIELDS(match1, match2, &out);
443c698e2adSLouis Peens 		if (out)
444c698e2adSLouis Peens 			goto check_failed;
445c698e2adSLouis Peens 	}
446c698e2adSLouis Peens 
4472b3082c6SRatheesh Kannoth 	if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_ENC_CONTROL)) {
448c698e2adSLouis Peens 		struct flow_match_control match1, match2;
449c698e2adSLouis Peens 
450c698e2adSLouis Peens 		flow_rule_match_enc_control(entry1->rule, &match1);
451c698e2adSLouis Peens 		flow_rule_match_enc_control(entry2->rule, &match2);
452c698e2adSLouis Peens 		COMPARE_UNMASKED_FIELDS(match1, match2, &out);
453c698e2adSLouis Peens 		if (out)
454c698e2adSLouis Peens 			goto check_failed;
455c698e2adSLouis Peens 	}
456c698e2adSLouis Peens 
4572b3082c6SRatheesh Kannoth 	if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_ENC_IP)) {
458c698e2adSLouis Peens 		struct flow_match_ip match1, match2;
459c698e2adSLouis Peens 
460c698e2adSLouis Peens 		flow_rule_match_enc_ip(entry1->rule, &match1);
461c698e2adSLouis Peens 		flow_rule_match_enc_ip(entry2->rule, &match2);
462c698e2adSLouis Peens 		COMPARE_UNMASKED_FIELDS(match1, match2, &out);
463c698e2adSLouis Peens 		if (out)
464c698e2adSLouis Peens 			goto check_failed;
465c698e2adSLouis Peens 	}
466c698e2adSLouis Peens 
4672b3082c6SRatheesh Kannoth 	if (ovlp_keys & BIT_ULL(FLOW_DISSECTOR_KEY_ENC_OPTS)) {
468c698e2adSLouis Peens 		struct flow_match_enc_opts match1, match2;
469c698e2adSLouis Peens 
470c698e2adSLouis Peens 		flow_rule_match_enc_opts(entry1->rule, &match1);
471c698e2adSLouis Peens 		flow_rule_match_enc_opts(entry2->rule, &match2);
472c698e2adSLouis Peens 		COMPARE_UNMASKED_FIELDS(match1, match2, &out);
473c698e2adSLouis Peens 		if (out)
474c698e2adSLouis Peens 			goto check_failed;
475c698e2adSLouis Peens 	}
476c698e2adSLouis Peens 
4773c863c30SLouis Peens 	return 0;
478c698e2adSLouis Peens 
479c698e2adSLouis Peens check_failed:
480c698e2adSLouis Peens 	return -EINVAL;
4813c863c30SLouis Peens }
4823c863c30SLouis Peens 
nfp_ct_check_vlan_merge(struct flow_action_entry * a_in,struct flow_rule * rule)483742b7072SHui Zhou static int nfp_ct_check_vlan_merge(struct flow_action_entry *a_in,
484742b7072SHui Zhou 				   struct flow_rule *rule)
485742b7072SHui Zhou {
486742b7072SHui Zhou 	struct flow_match_vlan match;
487742b7072SHui Zhou 
488742b7072SHui Zhou 	if (unlikely(flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CVLAN)))
489742b7072SHui Zhou 		return -EOPNOTSUPP;
490742b7072SHui Zhou 
491742b7072SHui Zhou 	/* post_ct does not match VLAN KEY, can be merged. */
492742b7072SHui Zhou 	if (likely(!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)))
493742b7072SHui Zhou 		return 0;
494742b7072SHui Zhou 
495742b7072SHui Zhou 	switch (a_in->id) {
496742b7072SHui Zhou 	/* pre_ct has pop vlan, post_ct cannot match VLAN KEY, cannot be merged. */
497742b7072SHui Zhou 	case FLOW_ACTION_VLAN_POP:
498742b7072SHui Zhou 		return -EOPNOTSUPP;
499742b7072SHui Zhou 
500742b7072SHui Zhou 	case FLOW_ACTION_VLAN_PUSH:
501742b7072SHui Zhou 	case FLOW_ACTION_VLAN_MANGLE:
502742b7072SHui Zhou 		flow_rule_match_vlan(rule, &match);
503742b7072SHui Zhou 		/* different vlan id, cannot be merged. */
504742b7072SHui Zhou 		if ((match.key->vlan_id & match.mask->vlan_id) ^
505742b7072SHui Zhou 		    (a_in->vlan.vid & match.mask->vlan_id))
506742b7072SHui Zhou 			return -EOPNOTSUPP;
507742b7072SHui Zhou 
508742b7072SHui Zhou 		/* different tpid, cannot be merged. */
509742b7072SHui Zhou 		if ((match.key->vlan_tpid & match.mask->vlan_tpid) ^
510742b7072SHui Zhou 		    (a_in->vlan.proto & match.mask->vlan_tpid))
511742b7072SHui Zhou 			return -EOPNOTSUPP;
512742b7072SHui Zhou 
513742b7072SHui Zhou 		/* different priority, cannot be merged. */
514742b7072SHui Zhou 		if ((match.key->vlan_priority & match.mask->vlan_priority) ^
515742b7072SHui Zhou 		    (a_in->vlan.prio & match.mask->vlan_priority))
516742b7072SHui Zhou 			return -EOPNOTSUPP;
517742b7072SHui Zhou 
518742b7072SHui Zhou 		break;
519742b7072SHui Zhou 	default:
520742b7072SHui Zhou 		return -EOPNOTSUPP;
521742b7072SHui Zhou 	}
522742b7072SHui Zhou 
523742b7072SHui Zhou 	return 0;
524742b7072SHui Zhou }
525742b7072SHui Zhou 
526a87ceb3dSWentao Jia /* Extra check for multiple ct-zones merge
527a87ceb3dSWentao Jia  * currently surpport nft entries merge check in different zones
528a87ceb3dSWentao Jia  */
nfp_ct_merge_extra_check(struct nfp_fl_ct_flow_entry * nft_entry,struct nfp_fl_ct_tc_merge * tc_m_entry)529a87ceb3dSWentao Jia static int nfp_ct_merge_extra_check(struct nfp_fl_ct_flow_entry *nft_entry,
530a87ceb3dSWentao Jia 				    struct nfp_fl_ct_tc_merge *tc_m_entry)
531a87ceb3dSWentao Jia {
532a87ceb3dSWentao Jia 	struct nfp_fl_nft_tc_merge *prev_nft_m_entry;
533a87ceb3dSWentao Jia 	struct nfp_fl_ct_flow_entry *pre_ct_entry;
534a87ceb3dSWentao Jia 
535a87ceb3dSWentao Jia 	pre_ct_entry = tc_m_entry->pre_ct_parent;
536a87ceb3dSWentao Jia 	prev_nft_m_entry = pre_ct_entry->prev_m_entries[pre_ct_entry->num_prev_m_entries - 1];
537a87ceb3dSWentao Jia 
538a87ceb3dSWentao Jia 	return nfp_ct_merge_check(prev_nft_m_entry->nft_parent, nft_entry);
539a87ceb3dSWentao Jia }
540a87ceb3dSWentao Jia 
nfp_ct_merge_act_check(struct nfp_fl_ct_flow_entry * pre_ct_entry,struct nfp_fl_ct_flow_entry * post_ct_entry,struct nfp_fl_ct_flow_entry * nft_entry)541a6ffdd3aSLouis Peens static int nfp_ct_merge_act_check(struct nfp_fl_ct_flow_entry *pre_ct_entry,
542a6ffdd3aSLouis Peens 				  struct nfp_fl_ct_flow_entry *post_ct_entry,
543a6ffdd3aSLouis Peens 				  struct nfp_fl_ct_flow_entry *nft_entry)
544a6ffdd3aSLouis Peens {
54530c4a9f4SLouis Peens 	struct flow_action_entry *act;
546742b7072SHui Zhou 	int i, err;
54730c4a9f4SLouis Peens 
54830c4a9f4SLouis Peens 	/* Check for pre_ct->action conflicts */
54930c4a9f4SLouis Peens 	flow_action_for_each(i, act, &pre_ct_entry->rule->action) {
55030c4a9f4SLouis Peens 		switch (act->id) {
55130c4a9f4SLouis Peens 		case FLOW_ACTION_VLAN_PUSH:
55230c4a9f4SLouis Peens 		case FLOW_ACTION_VLAN_POP:
55330c4a9f4SLouis Peens 		case FLOW_ACTION_VLAN_MANGLE:
554742b7072SHui Zhou 			err = nfp_ct_check_vlan_merge(act, post_ct_entry->rule);
555742b7072SHui Zhou 			if (err)
556742b7072SHui Zhou 				return err;
557742b7072SHui Zhou 			break;
55830c4a9f4SLouis Peens 		case FLOW_ACTION_MPLS_PUSH:
55930c4a9f4SLouis Peens 		case FLOW_ACTION_MPLS_POP:
56030c4a9f4SLouis Peens 		case FLOW_ACTION_MPLS_MANGLE:
56130c4a9f4SLouis Peens 			return -EOPNOTSUPP;
56230c4a9f4SLouis Peens 		default:
56330c4a9f4SLouis Peens 			break;
56430c4a9f4SLouis Peens 		}
56530c4a9f4SLouis Peens 	}
56630c4a9f4SLouis Peens 
56730c4a9f4SLouis Peens 	/* Check for nft->action conflicts */
56830c4a9f4SLouis Peens 	flow_action_for_each(i, act, &nft_entry->rule->action) {
56930c4a9f4SLouis Peens 		switch (act->id) {
57030c4a9f4SLouis Peens 		case FLOW_ACTION_VLAN_PUSH:
57130c4a9f4SLouis Peens 		case FLOW_ACTION_VLAN_POP:
57230c4a9f4SLouis Peens 		case FLOW_ACTION_VLAN_MANGLE:
57330c4a9f4SLouis Peens 		case FLOW_ACTION_MPLS_PUSH:
57430c4a9f4SLouis Peens 		case FLOW_ACTION_MPLS_POP:
57530c4a9f4SLouis Peens 		case FLOW_ACTION_MPLS_MANGLE:
57630c4a9f4SLouis Peens 			return -EOPNOTSUPP;
57730c4a9f4SLouis Peens 		default:
57830c4a9f4SLouis Peens 			break;
57930c4a9f4SLouis Peens 		}
58030c4a9f4SLouis Peens 	}
581a6ffdd3aSLouis Peens 	return 0;
582a6ffdd3aSLouis Peens }
583a6ffdd3aSLouis Peens 
nfp_ct_check_meta(struct nfp_fl_ct_flow_entry * post_ct_entry,struct nfp_fl_ct_flow_entry * nft_entry)584a6ffdd3aSLouis Peens static int nfp_ct_check_meta(struct nfp_fl_ct_flow_entry *post_ct_entry,
585a6ffdd3aSLouis Peens 			     struct nfp_fl_ct_flow_entry *nft_entry)
586a6ffdd3aSLouis Peens {
5875e5f0816SLouis Peens 	struct flow_dissector *dissector = post_ct_entry->rule->match.dissector;
5885e5f0816SLouis Peens 	struct flow_action_entry *ct_met;
5895e5f0816SLouis Peens 	struct flow_match_ct ct;
5905e5f0816SLouis Peens 	int i;
5915e5f0816SLouis Peens 
5925e5f0816SLouis Peens 	ct_met = get_flow_act(nft_entry->rule, FLOW_ACTION_CT_METADATA);
5932b3082c6SRatheesh Kannoth 	if (ct_met && (dissector->used_keys & BIT_ULL(FLOW_DISSECTOR_KEY_CT))) {
5945e5f0816SLouis Peens 		u32 *act_lbl;
5955e5f0816SLouis Peens 
5965e5f0816SLouis Peens 		act_lbl = ct_met->ct_metadata.labels;
5975e5f0816SLouis Peens 		flow_rule_match_ct(post_ct_entry->rule, &ct);
5985e5f0816SLouis Peens 		for (i = 0; i < 4; i++) {
5995e5f0816SLouis Peens 			if ((ct.key->ct_labels[i] & ct.mask->ct_labels[i]) ^
6005e5f0816SLouis Peens 			    (act_lbl[i] & ct.mask->ct_labels[i]))
6015e5f0816SLouis Peens 				return -EINVAL;
6025e5f0816SLouis Peens 		}
6035e5f0816SLouis Peens 
6045e5f0816SLouis Peens 		if ((ct.key->ct_mark & ct.mask->ct_mark) ^
6055e5f0816SLouis Peens 		    (ct_met->ct_metadata.mark & ct.mask->ct_mark))
6065e5f0816SLouis Peens 			return -EINVAL;
6075e5f0816SLouis Peens 
608a6ffdd3aSLouis Peens 		return 0;
6095cee92c6SHui Zhou 	} else {
6105cee92c6SHui Zhou 		/* post_ct with ct clear action will not match the
6115cee92c6SHui Zhou 		 * ct status when nft is nat entry.
6125cee92c6SHui Zhou 		 */
6135cee92c6SHui Zhou 		if (nft_entry->flags & NFP_FL_ACTION_DO_MANGLE)
6145cee92c6SHui Zhou 			return 0;
615a6ffdd3aSLouis Peens 	}
616a6ffdd3aSLouis Peens 
6175e5f0816SLouis Peens 	return -EINVAL;
6185e5f0816SLouis Peens }
6195e5f0816SLouis Peens 
62071e88cfbSLouis Peens static int
nfp_fl_calc_key_layers_sz(struct nfp_fl_key_ls in_key_ls,uint16_t * map)62171e88cfbSLouis Peens nfp_fl_calc_key_layers_sz(struct nfp_fl_key_ls in_key_ls, uint16_t *map)
62271e88cfbSLouis Peens {
62371e88cfbSLouis Peens 	int key_size;
62471e88cfbSLouis Peens 
62571e88cfbSLouis Peens 	/* This field must always be present */
62671e88cfbSLouis Peens 	key_size = sizeof(struct nfp_flower_meta_tci);
62771e88cfbSLouis Peens 	map[FLOW_PAY_META_TCI] = 0;
62871e88cfbSLouis Peens 
62971e88cfbSLouis Peens 	if (in_key_ls.key_layer & NFP_FLOWER_LAYER_EXT_META) {
63071e88cfbSLouis Peens 		map[FLOW_PAY_EXT_META] = key_size;
63171e88cfbSLouis Peens 		key_size += sizeof(struct nfp_flower_ext_meta);
63271e88cfbSLouis Peens 	}
63371e88cfbSLouis Peens 	if (in_key_ls.key_layer & NFP_FLOWER_LAYER_PORT) {
63471e88cfbSLouis Peens 		map[FLOW_PAY_INPORT] = key_size;
63571e88cfbSLouis Peens 		key_size += sizeof(struct nfp_flower_in_port);
63671e88cfbSLouis Peens 	}
63771e88cfbSLouis Peens 	if (in_key_ls.key_layer & NFP_FLOWER_LAYER_MAC) {
63871e88cfbSLouis Peens 		map[FLOW_PAY_MAC_MPLS] = key_size;
63971e88cfbSLouis Peens 		key_size += sizeof(struct nfp_flower_mac_mpls);
64071e88cfbSLouis Peens 	}
64171e88cfbSLouis Peens 	if (in_key_ls.key_layer & NFP_FLOWER_LAYER_TP) {
64271e88cfbSLouis Peens 		map[FLOW_PAY_L4] = key_size;
64371e88cfbSLouis Peens 		key_size += sizeof(struct nfp_flower_tp_ports);
64471e88cfbSLouis Peens 	}
64571e88cfbSLouis Peens 	if (in_key_ls.key_layer & NFP_FLOWER_LAYER_IPV4) {
64671e88cfbSLouis Peens 		map[FLOW_PAY_IPV4] = key_size;
64771e88cfbSLouis Peens 		key_size += sizeof(struct nfp_flower_ipv4);
64871e88cfbSLouis Peens 	}
64971e88cfbSLouis Peens 	if (in_key_ls.key_layer & NFP_FLOWER_LAYER_IPV6) {
65071e88cfbSLouis Peens 		map[FLOW_PAY_IPV6] = key_size;
65171e88cfbSLouis Peens 		key_size += sizeof(struct nfp_flower_ipv6);
65271e88cfbSLouis Peens 	}
65371e88cfbSLouis Peens 
654a0b84334SEtienne van der Linde 	if (in_key_ls.key_layer_two & NFP_FLOWER_LAYER2_QINQ) {
655a0b84334SEtienne van der Linde 		map[FLOW_PAY_QINQ] = key_size;
656a0b84334SEtienne van der Linde 		key_size += sizeof(struct nfp_flower_vlan);
657a0b84334SEtienne van der Linde 	}
658a0b84334SEtienne van der Linde 
65971e88cfbSLouis Peens 	if (in_key_ls.key_layer_two & NFP_FLOWER_LAYER2_GRE) {
66071e88cfbSLouis Peens 		map[FLOW_PAY_GRE] = key_size;
66171e88cfbSLouis Peens 		if (in_key_ls.key_layer_two & NFP_FLOWER_LAYER2_TUN_IPV6)
66271e88cfbSLouis Peens 			key_size += sizeof(struct nfp_flower_ipv6_gre_tun);
66371e88cfbSLouis Peens 		else
66471e88cfbSLouis Peens 			key_size += sizeof(struct nfp_flower_ipv4_gre_tun);
66571e88cfbSLouis Peens 	}
66671e88cfbSLouis Peens 
66771e88cfbSLouis Peens 	if ((in_key_ls.key_layer & NFP_FLOWER_LAYER_VXLAN) ||
66871e88cfbSLouis Peens 	    (in_key_ls.key_layer_two & NFP_FLOWER_LAYER2_GENEVE)) {
66971e88cfbSLouis Peens 		map[FLOW_PAY_UDP_TUN] = key_size;
67071e88cfbSLouis Peens 		if (in_key_ls.key_layer_two & NFP_FLOWER_LAYER2_TUN_IPV6)
67171e88cfbSLouis Peens 			key_size += sizeof(struct nfp_flower_ipv6_udp_tun);
67271e88cfbSLouis Peens 		else
67371e88cfbSLouis Peens 			key_size += sizeof(struct nfp_flower_ipv4_udp_tun);
67471e88cfbSLouis Peens 	}
67571e88cfbSLouis Peens 
67671e88cfbSLouis Peens 	if (in_key_ls.key_layer_two & NFP_FLOWER_LAYER2_GENEVE_OP) {
67771e88cfbSLouis Peens 		map[FLOW_PAY_GENEVE_OPT] = key_size;
67871e88cfbSLouis Peens 		key_size += sizeof(struct nfp_flower_geneve_options);
67971e88cfbSLouis Peens 	}
68071e88cfbSLouis Peens 
68171e88cfbSLouis Peens 	return key_size;
68271e88cfbSLouis Peens }
68371e88cfbSLouis Peens 
6845cee92c6SHui Zhou /* get the csum flag according the ip proto and mangle action. */
nfp_fl_get_csum_flag(struct flow_action_entry * a_in,u8 ip_proto,u32 * csum)6855cee92c6SHui Zhou static void nfp_fl_get_csum_flag(struct flow_action_entry *a_in, u8 ip_proto, u32 *csum)
6865cee92c6SHui Zhou {
6875cee92c6SHui Zhou 	if (a_in->id != FLOW_ACTION_MANGLE)
6885cee92c6SHui Zhou 		return;
6895cee92c6SHui Zhou 
6905cee92c6SHui Zhou 	switch (a_in->mangle.htype) {
6915cee92c6SHui Zhou 	case FLOW_ACT_MANGLE_HDR_TYPE_IP4:
6925cee92c6SHui Zhou 		*csum |= TCA_CSUM_UPDATE_FLAG_IPV4HDR;
6935cee92c6SHui Zhou 		if (ip_proto == IPPROTO_TCP)
6945cee92c6SHui Zhou 			*csum |= TCA_CSUM_UPDATE_FLAG_TCP;
6955cee92c6SHui Zhou 		else if (ip_proto == IPPROTO_UDP)
6965cee92c6SHui Zhou 			*csum |= TCA_CSUM_UPDATE_FLAG_UDP;
6975cee92c6SHui Zhou 		break;
6985cee92c6SHui Zhou 	case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
6995cee92c6SHui Zhou 		*csum |= TCA_CSUM_UPDATE_FLAG_TCP;
7005cee92c6SHui Zhou 		break;
7015cee92c6SHui Zhou 	case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
7025cee92c6SHui Zhou 		*csum |= TCA_CSUM_UPDATE_FLAG_UDP;
7035cee92c6SHui Zhou 		break;
7045cee92c6SHui Zhou 	default:
7055cee92c6SHui Zhou 		break;
7065cee92c6SHui Zhou 	}
7075cee92c6SHui Zhou }
7085cee92c6SHui Zhou 
nfp_fl_merge_actions_offload(struct flow_rule ** rules,struct nfp_flower_priv * priv,struct net_device * netdev,struct nfp_fl_payload * flow_pay,int num_rules)709d94a63b4SLouis Peens static int nfp_fl_merge_actions_offload(struct flow_rule **rules,
710d94a63b4SLouis Peens 					struct nfp_flower_priv *priv,
711d94a63b4SLouis Peens 					struct net_device *netdev,
71246a83c85SWentao Jia 					struct nfp_fl_payload *flow_pay,
71346a83c85SWentao Jia 					int num_rules)
714d94a63b4SLouis Peens {
7155cee92c6SHui Zhou 	enum flow_action_hw_stats tmp_stats = FLOW_ACTION_HW_STATS_DONT_CARE;
716d94a63b4SLouis Peens 	struct flow_action_entry *a_in;
71746a83c85SWentao Jia 	int i, j, id, num_actions = 0;
718d94a63b4SLouis Peens 	struct flow_rule *a_rule;
719d94a63b4SLouis Peens 	int err = 0, offset = 0;
720d94a63b4SLouis Peens 
72146a83c85SWentao Jia 	for (i = 0; i < num_rules; i++)
72246a83c85SWentao Jia 		num_actions += rules[i]->action.num_entries;
723d94a63b4SLouis Peens 
7245cee92c6SHui Zhou 	/* Add one action to make sure there is enough room to add an checksum action
7255cee92c6SHui Zhou 	 * when do nat.
7265cee92c6SHui Zhou 	 */
72746a83c85SWentao Jia 	a_rule = flow_rule_alloc(num_actions + (num_rules / 2));
728d94a63b4SLouis Peens 	if (!a_rule)
729d94a63b4SLouis Peens 		return -ENOMEM;
730d94a63b4SLouis Peens 
7315cee92c6SHui Zhou 	/* post_ct entry have one action at least. */
73246a83c85SWentao Jia 	if (rules[num_rules - 1]->action.num_entries != 0)
73346a83c85SWentao Jia 		tmp_stats = rules[num_rules - 1]->action.entries[0].hw_stats;
73446a83c85SWentao Jia 
73546a83c85SWentao Jia 	/* Actions need a BASIC dissector. */
73646a83c85SWentao Jia 	a_rule->match = rules[0]->match;
737d94a63b4SLouis Peens 
738d94a63b4SLouis Peens 	/* Copy actions */
73946a83c85SWentao Jia 	for (j = 0; j < num_rules; j++) {
7405cee92c6SHui Zhou 		u32 csum_updated = 0;
7415cee92c6SHui Zhou 		u8 ip_proto = 0;
7425cee92c6SHui Zhou 
743d94a63b4SLouis Peens 		if (flow_rule_match_key(rules[j], FLOW_DISSECTOR_KEY_BASIC)) {
744d94a63b4SLouis Peens 			struct flow_match_basic match;
745d94a63b4SLouis Peens 
7469bacb93bSWalter Heymans 			/* ip_proto is the only field that is needed in later compile_action,
747d94a63b4SLouis Peens 			 * needed to set the correct checksum flags. It doesn't really matter
748d94a63b4SLouis Peens 			 * which input rule's ip_proto field we take as the earlier merge checks
749d94a63b4SLouis Peens 			 * would have made sure that they don't conflict. We do not know which
750d94a63b4SLouis Peens 			 * of the subflows would have the ip_proto filled in, so we need to iterate
751d94a63b4SLouis Peens 			 * through the subflows and assign the proper subflow to a_rule
752d94a63b4SLouis Peens 			 */
753d94a63b4SLouis Peens 			flow_rule_match_basic(rules[j], &match);
7545cee92c6SHui Zhou 			if (match.mask->ip_proto) {
755d94a63b4SLouis Peens 				a_rule->match = rules[j]->match;
7565cee92c6SHui Zhou 				ip_proto = match.key->ip_proto;
7575cee92c6SHui Zhou 			}
758d94a63b4SLouis Peens 		}
759d94a63b4SLouis Peens 
760d94a63b4SLouis Peens 		for (i = 0; i < rules[j]->action.num_entries; i++) {
761d94a63b4SLouis Peens 			a_in = &rules[j]->action.entries[i];
762d94a63b4SLouis Peens 			id = a_in->id;
763d94a63b4SLouis Peens 
764d94a63b4SLouis Peens 			/* Ignore CT related actions as these would already have
765d94a63b4SLouis Peens 			 * been taken care of by previous checks, and we do not send
766d94a63b4SLouis Peens 			 * any CT actions to the firmware.
767d94a63b4SLouis Peens 			 */
768d94a63b4SLouis Peens 			switch (id) {
769d94a63b4SLouis Peens 			case FLOW_ACTION_CT:
770d94a63b4SLouis Peens 			case FLOW_ACTION_GOTO:
771d94a63b4SLouis Peens 			case FLOW_ACTION_CT_METADATA:
772d94a63b4SLouis Peens 				continue;
773d94a63b4SLouis Peens 			default:
7745cee92c6SHui Zhou 				/* nft entry is generated by tc ct, which mangle action do not care
7755cee92c6SHui Zhou 				 * the stats, inherit the post entry stats to meet the
7765cee92c6SHui Zhou 				 * flow_action_hw_stats_check.
77746a83c85SWentao Jia 				 * nft entry flow rules are at odd array index.
7785cee92c6SHui Zhou 				 */
77946a83c85SWentao Jia 				if (j & 0x01) {
7805cee92c6SHui Zhou 					if (a_in->hw_stats == FLOW_ACTION_HW_STATS_DONT_CARE)
7815cee92c6SHui Zhou 						a_in->hw_stats = tmp_stats;
7825cee92c6SHui Zhou 					nfp_fl_get_csum_flag(a_in, ip_proto, &csum_updated);
7835cee92c6SHui Zhou 				}
784d94a63b4SLouis Peens 				memcpy(&a_rule->action.entries[offset++],
785d94a63b4SLouis Peens 				       a_in, sizeof(struct flow_action_entry));
786d94a63b4SLouis Peens 				break;
787d94a63b4SLouis Peens 			}
788d94a63b4SLouis Peens 		}
7895cee92c6SHui Zhou 		/* nft entry have mangle action, but do not have checksum action when do NAT,
7905cee92c6SHui Zhou 		 * hardware will automatically fix IPv4 and TCP/UDP checksum. so add an csum action
7915cee92c6SHui Zhou 		 * to meet csum action check.
7925cee92c6SHui Zhou 		 */
7935cee92c6SHui Zhou 		if (csum_updated) {
7945cee92c6SHui Zhou 			struct flow_action_entry *csum_action;
7955cee92c6SHui Zhou 
7965cee92c6SHui Zhou 			csum_action = &a_rule->action.entries[offset++];
7975cee92c6SHui Zhou 			csum_action->id = FLOW_ACTION_CSUM;
7985cee92c6SHui Zhou 			csum_action->csum_flags = csum_updated;
7995cee92c6SHui Zhou 			csum_action->hw_stats = tmp_stats;
8005cee92c6SHui Zhou 		}
801d94a63b4SLouis Peens 	}
802d94a63b4SLouis Peens 
803d94a63b4SLouis Peens 	/* Some actions would have been ignored, so update the num_entries field */
804d94a63b4SLouis Peens 	a_rule->action.num_entries = offset;
805d94a63b4SLouis Peens 	err = nfp_flower_compile_action(priv->app, a_rule, netdev, flow_pay, NULL);
806d94a63b4SLouis Peens 	kfree(a_rule);
807d94a63b4SLouis Peens 
808d94a63b4SLouis Peens 	return err;
809d94a63b4SLouis Peens }
810d94a63b4SLouis Peens 
nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge * m_entry)811a6ffdd3aSLouis Peens static int nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge *m_entry)
812a6ffdd3aSLouis Peens {
81371e88cfbSLouis Peens 	enum nfp_flower_tun_type tun_type = NFP_FL_TUNNEL_NONE;
81471e88cfbSLouis Peens 	struct nfp_fl_ct_zone_entry *zt = m_entry->zt;
815a87ceb3dSWentao Jia 	struct flow_rule *rules[NFP_MAX_ENTRY_RULES];
816a87ceb3dSWentao Jia 	struct nfp_fl_ct_flow_entry *pre_ct_entry;
81771e88cfbSLouis Peens 	struct nfp_fl_key_ls key_layer, tmp_layer;
81871e88cfbSLouis Peens 	struct nfp_flower_priv *priv = zt->priv;
81971e88cfbSLouis Peens 	u16 key_map[_FLOW_PAY_LAYERS_MAX];
8205a2b9304SLouis Peens 	struct nfp_fl_payload *flow_pay;
8215a2b9304SLouis Peens 	u8 *key, *msk, *kdata, *mdata;
822453cdc30SLouis Peens 	struct nfp_port *port = NULL;
823a87ceb3dSWentao Jia 	int num_rules, err, i, j = 0;
8245a2b9304SLouis Peens 	struct net_device *netdev;
8255a2b9304SLouis Peens 	bool qinq_sup;
8265a2b9304SLouis Peens 	u32 port_id;
8275a2b9304SLouis Peens 	u16 offset;
82871e88cfbSLouis Peens 
8295a2b9304SLouis Peens 	netdev = m_entry->netdev;
8305a2b9304SLouis Peens 	qinq_sup = !!(priv->flower_ext_feats & NFP_FL_FEATS_VLAN_QINQ);
8315a2b9304SLouis Peens 
832a87ceb3dSWentao Jia 	pre_ct_entry = m_entry->tc_m_parent->pre_ct_parent;
833a87ceb3dSWentao Jia 	num_rules = pre_ct_entry->num_prev_m_entries * 2 + _CT_TYPE_MAX;
834a87ceb3dSWentao Jia 
835a87ceb3dSWentao Jia 	for (i = 0; i < pre_ct_entry->num_prev_m_entries; i++) {
836a87ceb3dSWentao Jia 		rules[j++] = pre_ct_entry->prev_m_entries[i]->tc_m_parent->pre_ct_parent->rule;
837a87ceb3dSWentao Jia 		rules[j++] = pre_ct_entry->prev_m_entries[i]->nft_parent->rule;
838a87ceb3dSWentao Jia 	}
839a87ceb3dSWentao Jia 
840a87ceb3dSWentao Jia 	rules[j++] = m_entry->tc_m_parent->pre_ct_parent->rule;
841a87ceb3dSWentao Jia 	rules[j++] = m_entry->nft_parent->rule;
842a87ceb3dSWentao Jia 	rules[j++] = m_entry->tc_m_parent->post_ct_parent->rule;
84371e88cfbSLouis Peens 
84471e88cfbSLouis Peens 	memset(&key_layer, 0, sizeof(struct nfp_fl_key_ls));
84571e88cfbSLouis Peens 	memset(&key_map, 0, sizeof(key_map));
84671e88cfbSLouis Peens 
84771e88cfbSLouis Peens 	/* Calculate the resultant key layer and size for offload */
84846a83c85SWentao Jia 	for (i = 0; i < num_rules; i++) {
84971e88cfbSLouis Peens 		err = nfp_flower_calculate_key_layers(priv->app,
85071e88cfbSLouis Peens 						      m_entry->netdev,
85171e88cfbSLouis Peens 						      &tmp_layer, rules[i],
85271e88cfbSLouis Peens 						      &tun_type, NULL);
85371e88cfbSLouis Peens 		if (err)
85471e88cfbSLouis Peens 			return err;
85571e88cfbSLouis Peens 
85671e88cfbSLouis Peens 		key_layer.key_layer |= tmp_layer.key_layer;
85771e88cfbSLouis Peens 		key_layer.key_layer_two |= tmp_layer.key_layer_two;
85871e88cfbSLouis Peens 	}
85971e88cfbSLouis Peens 	key_layer.key_size = nfp_fl_calc_key_layers_sz(key_layer, key_map);
86071e88cfbSLouis Peens 
8615a2b9304SLouis Peens 	flow_pay = nfp_flower_allocate_new(&key_layer);
8625a2b9304SLouis Peens 	if (!flow_pay)
8635a2b9304SLouis Peens 		return -ENOMEM;
8645a2b9304SLouis Peens 
8655a2b9304SLouis Peens 	memset(flow_pay->unmasked_data, 0, key_layer.key_size);
8665a2b9304SLouis Peens 	memset(flow_pay->mask_data, 0, key_layer.key_size);
8675a2b9304SLouis Peens 
8685a2b9304SLouis Peens 	kdata = flow_pay->unmasked_data;
8695a2b9304SLouis Peens 	mdata = flow_pay->mask_data;
8705a2b9304SLouis Peens 
8715a2b9304SLouis Peens 	offset = key_map[FLOW_PAY_META_TCI];
8725a2b9304SLouis Peens 	key = kdata + offset;
8735a2b9304SLouis Peens 	msk = mdata + offset;
8745a2b9304SLouis Peens 	nfp_flower_compile_meta((struct nfp_flower_meta_tci *)key,
8755a2b9304SLouis Peens 				(struct nfp_flower_meta_tci *)msk,
8765a2b9304SLouis Peens 				key_layer.key_layer);
8775a2b9304SLouis Peens 
8785a2b9304SLouis Peens 	if (NFP_FLOWER_LAYER_EXT_META & key_layer.key_layer) {
8795a2b9304SLouis Peens 		offset =  key_map[FLOW_PAY_EXT_META];
8805a2b9304SLouis Peens 		key = kdata + offset;
8815a2b9304SLouis Peens 		msk = mdata + offset;
8825a2b9304SLouis Peens 		nfp_flower_compile_ext_meta((struct nfp_flower_ext_meta *)key,
8835a2b9304SLouis Peens 					    key_layer.key_layer_two);
8845a2b9304SLouis Peens 		nfp_flower_compile_ext_meta((struct nfp_flower_ext_meta *)msk,
8855a2b9304SLouis Peens 					    key_layer.key_layer_two);
8865a2b9304SLouis Peens 	}
8875a2b9304SLouis Peens 
8885a2b9304SLouis Peens 	/* Using in_port from the -trk rule. The tc merge checks should already
8895a2b9304SLouis Peens 	 * be checking that the ingress netdevs are the same
8905a2b9304SLouis Peens 	 */
8915a2b9304SLouis Peens 	port_id = nfp_flower_get_port_id_from_netdev(priv->app, netdev);
8925a2b9304SLouis Peens 	offset = key_map[FLOW_PAY_INPORT];
8935a2b9304SLouis Peens 	key = kdata + offset;
8945a2b9304SLouis Peens 	msk = mdata + offset;
8955a2b9304SLouis Peens 	err = nfp_flower_compile_port((struct nfp_flower_in_port *)key,
8965a2b9304SLouis Peens 				      port_id, false, tun_type, NULL);
8975a2b9304SLouis Peens 	if (err)
8985a2b9304SLouis Peens 		goto ct_offload_err;
8995a2b9304SLouis Peens 	err = nfp_flower_compile_port((struct nfp_flower_in_port *)msk,
9005a2b9304SLouis Peens 				      port_id, true, tun_type, NULL);
9015a2b9304SLouis Peens 	if (err)
9025a2b9304SLouis Peens 		goto ct_offload_err;
9035a2b9304SLouis Peens 
9045a2b9304SLouis Peens 	/* This following part works on the assumption that previous checks has
9055a2b9304SLouis Peens 	 * already filtered out flows that has different values for the different
9065a2b9304SLouis Peens 	 * layers. Here we iterate through all three rules and merge their respective
9075a2b9304SLouis Peens 	 * masked value(cared bits), basic method is:
9085a2b9304SLouis Peens 	 * final_key = (r1_key & r1_mask) | (r2_key & r2_mask) | (r3_key & r3_mask)
9095a2b9304SLouis Peens 	 * final_mask = r1_mask | r2_mask | r3_mask
9105a2b9304SLouis Peens 	 * If none of the rules contains a match that is also fine, that simply means
9115a2b9304SLouis Peens 	 * that the layer is not present.
9125a2b9304SLouis Peens 	 */
9135a2b9304SLouis Peens 	if (!qinq_sup) {
91446a83c85SWentao Jia 		for (i = 0; i < num_rules; i++) {
9155a2b9304SLouis Peens 			offset = key_map[FLOW_PAY_META_TCI];
9165a2b9304SLouis Peens 			key = kdata + offset;
9175a2b9304SLouis Peens 			msk = mdata + offset;
9185a2b9304SLouis Peens 			nfp_flower_compile_tci((struct nfp_flower_meta_tci *)key,
9195a2b9304SLouis Peens 					       (struct nfp_flower_meta_tci *)msk,
9205a2b9304SLouis Peens 					       rules[i]);
9215a2b9304SLouis Peens 		}
9225a2b9304SLouis Peens 	}
9235a2b9304SLouis Peens 
9245a2b9304SLouis Peens 	if (NFP_FLOWER_LAYER_MAC & key_layer.key_layer) {
9255a2b9304SLouis Peens 		offset = key_map[FLOW_PAY_MAC_MPLS];
9265a2b9304SLouis Peens 		key = kdata + offset;
9275a2b9304SLouis Peens 		msk = mdata + offset;
92846a83c85SWentao Jia 		for (i = 0; i < num_rules; i++) {
9295a2b9304SLouis Peens 			nfp_flower_compile_mac((struct nfp_flower_mac_mpls *)key,
9305a2b9304SLouis Peens 					       (struct nfp_flower_mac_mpls *)msk,
9315a2b9304SLouis Peens 					       rules[i]);
9325a2b9304SLouis Peens 			err = nfp_flower_compile_mpls((struct nfp_flower_mac_mpls *)key,
9335a2b9304SLouis Peens 						      (struct nfp_flower_mac_mpls *)msk,
9345a2b9304SLouis Peens 						      rules[i], NULL);
9355a2b9304SLouis Peens 			if (err)
9365a2b9304SLouis Peens 				goto ct_offload_err;
9375a2b9304SLouis Peens 		}
9385a2b9304SLouis Peens 	}
9395a2b9304SLouis Peens 
9405a2b9304SLouis Peens 	if (NFP_FLOWER_LAYER_IPV4 & key_layer.key_layer) {
9415a2b9304SLouis Peens 		offset = key_map[FLOW_PAY_IPV4];
9425a2b9304SLouis Peens 		key = kdata + offset;
9435a2b9304SLouis Peens 		msk = mdata + offset;
94446a83c85SWentao Jia 		for (i = 0; i < num_rules; i++) {
9455a2b9304SLouis Peens 			nfp_flower_compile_ipv4((struct nfp_flower_ipv4 *)key,
9465a2b9304SLouis Peens 						(struct nfp_flower_ipv4 *)msk,
9475a2b9304SLouis Peens 						rules[i]);
9485a2b9304SLouis Peens 		}
9495a2b9304SLouis Peens 	}
9505a2b9304SLouis Peens 
9515a2b9304SLouis Peens 	if (NFP_FLOWER_LAYER_IPV6 & key_layer.key_layer) {
9525a2b9304SLouis Peens 		offset = key_map[FLOW_PAY_IPV6];
9535a2b9304SLouis Peens 		key = kdata + offset;
9545a2b9304SLouis Peens 		msk = mdata + offset;
95546a83c85SWentao Jia 		for (i = 0; i < num_rules; i++) {
9565a2b9304SLouis Peens 			nfp_flower_compile_ipv6((struct nfp_flower_ipv6 *)key,
9575a2b9304SLouis Peens 						(struct nfp_flower_ipv6 *)msk,
9585a2b9304SLouis Peens 						rules[i]);
9595a2b9304SLouis Peens 		}
9605a2b9304SLouis Peens 	}
9615a2b9304SLouis Peens 
9625a2b9304SLouis Peens 	if (NFP_FLOWER_LAYER_TP & key_layer.key_layer) {
9635a2b9304SLouis Peens 		offset = key_map[FLOW_PAY_L4];
9645a2b9304SLouis Peens 		key = kdata + offset;
9655a2b9304SLouis Peens 		msk = mdata + offset;
96646a83c85SWentao Jia 		for (i = 0; i < num_rules; i++) {
9675a2b9304SLouis Peens 			nfp_flower_compile_tport((struct nfp_flower_tp_ports *)key,
9685a2b9304SLouis Peens 						 (struct nfp_flower_tp_ports *)msk,
9695a2b9304SLouis Peens 						 rules[i]);
9705a2b9304SLouis Peens 		}
9715a2b9304SLouis Peens 	}
9725a2b9304SLouis Peens 
973a0b84334SEtienne van der Linde 	if (NFP_FLOWER_LAYER2_QINQ & key_layer.key_layer_two) {
974a0b84334SEtienne van der Linde 		offset = key_map[FLOW_PAY_QINQ];
975a0b84334SEtienne van der Linde 		key = kdata + offset;
976a0b84334SEtienne van der Linde 		msk = mdata + offset;
97746a83c85SWentao Jia 		for (i = 0; i < num_rules; i++) {
978a0b84334SEtienne van der Linde 			nfp_flower_compile_vlan((struct nfp_flower_vlan *)key,
979a0b84334SEtienne van der Linde 						(struct nfp_flower_vlan *)msk,
980a0b84334SEtienne van der Linde 						rules[i]);
981a0b84334SEtienne van der Linde 		}
982a0b84334SEtienne van der Linde 	}
983a0b84334SEtienne van der Linde 
9845a2b9304SLouis Peens 	if (key_layer.key_layer_two & NFP_FLOWER_LAYER2_GRE) {
9855a2b9304SLouis Peens 		offset = key_map[FLOW_PAY_GRE];
9865a2b9304SLouis Peens 		key = kdata + offset;
9875a2b9304SLouis Peens 		msk = mdata + offset;
9885a2b9304SLouis Peens 		if (key_layer.key_layer_two & NFP_FLOWER_LAYER2_TUN_IPV6) {
9895a2b9304SLouis Peens 			struct nfp_flower_ipv6_gre_tun *gre_match;
9905a2b9304SLouis Peens 			struct nfp_ipv6_addr_entry *entry;
9915a2b9304SLouis Peens 			struct in6_addr *dst;
9925a2b9304SLouis Peens 
99346a83c85SWentao Jia 			for (i = 0; i < num_rules; i++) {
9945a2b9304SLouis Peens 				nfp_flower_compile_ipv6_gre_tun((void *)key,
9955a2b9304SLouis Peens 								(void *)msk, rules[i]);
9965a2b9304SLouis Peens 			}
9975a2b9304SLouis Peens 			gre_match = (struct nfp_flower_ipv6_gre_tun *)key;
9985a2b9304SLouis Peens 			dst = &gre_match->ipv6.dst;
9995a2b9304SLouis Peens 
10005a2b9304SLouis Peens 			entry = nfp_tunnel_add_ipv6_off(priv->app, dst);
1001d80f6d66SYang Yingliang 			if (!entry) {
1002d80f6d66SYang Yingliang 				err = -ENOMEM;
10035a2b9304SLouis Peens 				goto ct_offload_err;
1004d80f6d66SYang Yingliang 			}
10055a2b9304SLouis Peens 
10065a2b9304SLouis Peens 			flow_pay->nfp_tun_ipv6 = entry;
10075a2b9304SLouis Peens 		} else {
10085a2b9304SLouis Peens 			__be32 dst;
10095a2b9304SLouis Peens 
101046a83c85SWentao Jia 			for (i = 0; i < num_rules; i++) {
10115a2b9304SLouis Peens 				nfp_flower_compile_ipv4_gre_tun((void *)key,
10125a2b9304SLouis Peens 								(void *)msk, rules[i]);
10135a2b9304SLouis Peens 			}
10145a2b9304SLouis Peens 			dst = ((struct nfp_flower_ipv4_gre_tun *)key)->ipv4.dst;
10155a2b9304SLouis Peens 
10165a2b9304SLouis Peens 			/* Store the tunnel destination in the rule data.
10175a2b9304SLouis Peens 			 * This must be present and be an exact match.
10185a2b9304SLouis Peens 			 */
10195a2b9304SLouis Peens 			flow_pay->nfp_tun_ipv4_addr = dst;
10205a2b9304SLouis Peens 			nfp_tunnel_add_ipv4_off(priv->app, dst);
10215a2b9304SLouis Peens 		}
10225a2b9304SLouis Peens 	}
10235a2b9304SLouis Peens 
10245a2b9304SLouis Peens 	if (key_layer.key_layer & NFP_FLOWER_LAYER_VXLAN ||
10255a2b9304SLouis Peens 	    key_layer.key_layer_two & NFP_FLOWER_LAYER2_GENEVE) {
10265a2b9304SLouis Peens 		offset = key_map[FLOW_PAY_UDP_TUN];
10275a2b9304SLouis Peens 		key = kdata + offset;
10285a2b9304SLouis Peens 		msk = mdata + offset;
10295a2b9304SLouis Peens 		if (key_layer.key_layer_two & NFP_FLOWER_LAYER2_TUN_IPV6) {
10305a2b9304SLouis Peens 			struct nfp_flower_ipv6_udp_tun *udp_match;
10315a2b9304SLouis Peens 			struct nfp_ipv6_addr_entry *entry;
10325a2b9304SLouis Peens 			struct in6_addr *dst;
10335a2b9304SLouis Peens 
103446a83c85SWentao Jia 			for (i = 0; i < num_rules; i++) {
10355a2b9304SLouis Peens 				nfp_flower_compile_ipv6_udp_tun((void *)key,
10365a2b9304SLouis Peens 								(void *)msk, rules[i]);
10375a2b9304SLouis Peens 			}
10385a2b9304SLouis Peens 			udp_match = (struct nfp_flower_ipv6_udp_tun *)key;
10395a2b9304SLouis Peens 			dst = &udp_match->ipv6.dst;
10405a2b9304SLouis Peens 
10415a2b9304SLouis Peens 			entry = nfp_tunnel_add_ipv6_off(priv->app, dst);
1042d80f6d66SYang Yingliang 			if (!entry) {
1043d80f6d66SYang Yingliang 				err = -ENOMEM;
10445a2b9304SLouis Peens 				goto ct_offload_err;
1045d80f6d66SYang Yingliang 			}
10465a2b9304SLouis Peens 
10475a2b9304SLouis Peens 			flow_pay->nfp_tun_ipv6 = entry;
10485a2b9304SLouis Peens 		} else {
10495a2b9304SLouis Peens 			__be32 dst;
10505a2b9304SLouis Peens 
105146a83c85SWentao Jia 			for (i = 0; i < num_rules; i++) {
10525a2b9304SLouis Peens 				nfp_flower_compile_ipv4_udp_tun((void *)key,
10535a2b9304SLouis Peens 								(void *)msk, rules[i]);
10545a2b9304SLouis Peens 			}
10555a2b9304SLouis Peens 			dst = ((struct nfp_flower_ipv4_udp_tun *)key)->ipv4.dst;
10565a2b9304SLouis Peens 
10575a2b9304SLouis Peens 			/* Store the tunnel destination in the rule data.
10585a2b9304SLouis Peens 			 * This must be present and be an exact match.
10595a2b9304SLouis Peens 			 */
10605a2b9304SLouis Peens 			flow_pay->nfp_tun_ipv4_addr = dst;
10615a2b9304SLouis Peens 			nfp_tunnel_add_ipv4_off(priv->app, dst);
10625a2b9304SLouis Peens 		}
10635a2b9304SLouis Peens 
10645a2b9304SLouis Peens 		if (key_layer.key_layer_two & NFP_FLOWER_LAYER2_GENEVE_OP) {
10655a2b9304SLouis Peens 			offset = key_map[FLOW_PAY_GENEVE_OPT];
10665a2b9304SLouis Peens 			key = kdata + offset;
10675a2b9304SLouis Peens 			msk = mdata + offset;
106846a83c85SWentao Jia 			for (i = 0; i < num_rules; i++)
10695a2b9304SLouis Peens 				nfp_flower_compile_geneve_opt(key, msk, rules[i]);
10705a2b9304SLouis Peens 		}
10715a2b9304SLouis Peens 	}
10725a2b9304SLouis Peens 
1073d94a63b4SLouis Peens 	/* Merge actions into flow_pay */
107446a83c85SWentao Jia 	err = nfp_fl_merge_actions_offload(rules, priv, netdev, flow_pay, num_rules);
1075d94a63b4SLouis Peens 	if (err)
1076d94a63b4SLouis Peens 		goto ct_offload_err;
1077d94a63b4SLouis Peens 
1078453cdc30SLouis Peens 	/* Use the pointer address as the cookie, but set the last bit to 1.
1079453cdc30SLouis Peens 	 * This is to avoid the 'is_merge_flow' check from detecting this as
1080453cdc30SLouis Peens 	 * an already merged flow. This works since address alignment means
1081453cdc30SLouis Peens 	 * that the last bit for pointer addresses will be 0.
1082453cdc30SLouis Peens 	 */
1083453cdc30SLouis Peens 	flow_pay->tc_flower_cookie = ((unsigned long)flow_pay) | 0x1;
1084453cdc30SLouis Peens 	err = nfp_compile_flow_metadata(priv->app, flow_pay->tc_flower_cookie,
1085453cdc30SLouis Peens 					flow_pay, netdev, NULL);
1086453cdc30SLouis Peens 	if (err)
1087453cdc30SLouis Peens 		goto ct_offload_err;
1088453cdc30SLouis Peens 
1089453cdc30SLouis Peens 	if (nfp_netdev_is_nfp_repr(netdev))
1090453cdc30SLouis Peens 		port = nfp_port_from_netdev(netdev);
1091453cdc30SLouis Peens 
1092453cdc30SLouis Peens 	err = rhashtable_insert_fast(&priv->flow_table, &flow_pay->fl_node,
1093453cdc30SLouis Peens 				     nfp_flower_table_params);
1094453cdc30SLouis Peens 	if (err)
1095453cdc30SLouis Peens 		goto ct_release_offload_meta_err;
1096453cdc30SLouis Peens 
1097400a5e5fSLouis Peens 	err = nfp_flower_xmit_flow(priv->app, flow_pay,
1098400a5e5fSLouis Peens 				   NFP_FLOWER_CMSG_TYPE_FLOW_ADD);
1099400a5e5fSLouis Peens 	if (err)
1100400a5e5fSLouis Peens 		goto ct_remove_rhash_err;
1101400a5e5fSLouis Peens 
1102453cdc30SLouis Peens 	m_entry->tc_flower_cookie = flow_pay->tc_flower_cookie;
1103453cdc30SLouis Peens 	m_entry->flow_pay = flow_pay;
1104453cdc30SLouis Peens 
1105453cdc30SLouis Peens 	if (port)
1106453cdc30SLouis Peens 		port->tc_offload_cnt++;
1107453cdc30SLouis Peens 
1108453cdc30SLouis Peens 	return err;
1109453cdc30SLouis Peens 
1110400a5e5fSLouis Peens ct_remove_rhash_err:
1111400a5e5fSLouis Peens 	WARN_ON_ONCE(rhashtable_remove_fast(&priv->flow_table,
1112400a5e5fSLouis Peens 					    &flow_pay->fl_node,
1113400a5e5fSLouis Peens 					    nfp_flower_table_params));
1114453cdc30SLouis Peens ct_release_offload_meta_err:
1115453cdc30SLouis Peens 	nfp_modify_flow_metadata(priv->app, flow_pay);
11165a2b9304SLouis Peens ct_offload_err:
1117453cdc30SLouis Peens 	if (flow_pay->nfp_tun_ipv4_addr)
1118453cdc30SLouis Peens 		nfp_tunnel_del_ipv4_off(priv->app, flow_pay->nfp_tun_ipv4_addr);
1119453cdc30SLouis Peens 	if (flow_pay->nfp_tun_ipv6)
1120453cdc30SLouis Peens 		nfp_tunnel_put_ipv6_off(priv->app, flow_pay->nfp_tun_ipv6);
11215a2b9304SLouis Peens 	kfree(flow_pay->action_data);
11225a2b9304SLouis Peens 	kfree(flow_pay->mask_data);
11235a2b9304SLouis Peens 	kfree(flow_pay->unmasked_data);
11245a2b9304SLouis Peens 	kfree(flow_pay);
11255a2b9304SLouis Peens 	return err;
1126a6ffdd3aSLouis Peens }
1127a6ffdd3aSLouis Peens 
nfp_fl_ct_del_offload(struct nfp_app * app,unsigned long cookie,struct net_device * netdev)1128a6ffdd3aSLouis Peens static int nfp_fl_ct_del_offload(struct nfp_app *app, unsigned long cookie,
1129a6ffdd3aSLouis Peens 				 struct net_device *netdev)
1130a6ffdd3aSLouis Peens {
1131453cdc30SLouis Peens 	struct nfp_flower_priv *priv = app->priv;
1132453cdc30SLouis Peens 	struct nfp_fl_payload *flow_pay;
1133453cdc30SLouis Peens 	struct nfp_port *port = NULL;
1134453cdc30SLouis Peens 	int err = 0;
1135453cdc30SLouis Peens 
1136453cdc30SLouis Peens 	if (nfp_netdev_is_nfp_repr(netdev))
1137453cdc30SLouis Peens 		port = nfp_port_from_netdev(netdev);
1138453cdc30SLouis Peens 
1139453cdc30SLouis Peens 	flow_pay = nfp_flower_search_fl_table(app, cookie, netdev);
1140453cdc30SLouis Peens 	if (!flow_pay)
1141453cdc30SLouis Peens 		return -ENOENT;
1142453cdc30SLouis Peens 
1143453cdc30SLouis Peens 	err = nfp_modify_flow_metadata(app, flow_pay);
1144453cdc30SLouis Peens 	if (err)
1145453cdc30SLouis Peens 		goto err_free_merge_flow;
1146453cdc30SLouis Peens 
1147453cdc30SLouis Peens 	if (flow_pay->nfp_tun_ipv4_addr)
1148453cdc30SLouis Peens 		nfp_tunnel_del_ipv4_off(app, flow_pay->nfp_tun_ipv4_addr);
1149453cdc30SLouis Peens 
1150453cdc30SLouis Peens 	if (flow_pay->nfp_tun_ipv6)
1151453cdc30SLouis Peens 		nfp_tunnel_put_ipv6_off(app, flow_pay->nfp_tun_ipv6);
1152453cdc30SLouis Peens 
1153453cdc30SLouis Peens 	if (!flow_pay->in_hw) {
1154453cdc30SLouis Peens 		err = 0;
1155453cdc30SLouis Peens 		goto err_free_merge_flow;
1156453cdc30SLouis Peens 	}
1157453cdc30SLouis Peens 
1158400a5e5fSLouis Peens 	err = nfp_flower_xmit_flow(app, flow_pay,
1159400a5e5fSLouis Peens 				   NFP_FLOWER_CMSG_TYPE_FLOW_DEL);
1160400a5e5fSLouis Peens 
1161453cdc30SLouis Peens err_free_merge_flow:
1162453cdc30SLouis Peens 	nfp_flower_del_linked_merge_flows(app, flow_pay);
1163453cdc30SLouis Peens 	if (port)
1164453cdc30SLouis Peens 		port->tc_offload_cnt--;
1165453cdc30SLouis Peens 	kfree(flow_pay->action_data);
1166453cdc30SLouis Peens 	kfree(flow_pay->mask_data);
1167453cdc30SLouis Peens 	kfree(flow_pay->unmasked_data);
1168453cdc30SLouis Peens 	WARN_ON_ONCE(rhashtable_remove_fast(&priv->flow_table,
1169453cdc30SLouis Peens 					    &flow_pay->fl_node,
1170453cdc30SLouis Peens 					    nfp_flower_table_params));
1171453cdc30SLouis Peens 	kfree_rcu(flow_pay, rcu);
1172453cdc30SLouis Peens 	return err;
1173a6ffdd3aSLouis Peens }
1174a6ffdd3aSLouis Peens 
nfp_ct_do_nft_merge(struct nfp_fl_ct_zone_entry * zt,struct nfp_fl_ct_flow_entry * nft_entry,struct nfp_fl_ct_tc_merge * tc_m_entry)1175a6ffdd3aSLouis Peens static int nfp_ct_do_nft_merge(struct nfp_fl_ct_zone_entry *zt,
1176a6ffdd3aSLouis Peens 			       struct nfp_fl_ct_flow_entry *nft_entry,
1177a6ffdd3aSLouis Peens 			       struct nfp_fl_ct_tc_merge *tc_m_entry)
1178a6ffdd3aSLouis Peens {
1179a6ffdd3aSLouis Peens 	struct nfp_fl_ct_flow_entry *post_ct_entry, *pre_ct_entry;
1180a6ffdd3aSLouis Peens 	struct nfp_fl_nft_tc_merge *nft_m_entry;
1181a6ffdd3aSLouis Peens 	unsigned long new_cookie[3];
1182a6ffdd3aSLouis Peens 	int err;
1183a6ffdd3aSLouis Peens 
1184a6ffdd3aSLouis Peens 	pre_ct_entry = tc_m_entry->pre_ct_parent;
1185a6ffdd3aSLouis Peens 	post_ct_entry = tc_m_entry->post_ct_parent;
1186a6ffdd3aSLouis Peens 
1187a6ffdd3aSLouis Peens 	err = nfp_ct_merge_act_check(pre_ct_entry, post_ct_entry, nft_entry);
1188a6ffdd3aSLouis Peens 	if (err)
1189a6ffdd3aSLouis Peens 		return err;
1190a6ffdd3aSLouis Peens 
1191a6ffdd3aSLouis Peens 	/* Check that the two tc flows are also compatible with
1192a6ffdd3aSLouis Peens 	 * the nft entry. No need to check the pre_ct and post_ct
1193a6ffdd3aSLouis Peens 	 * entries as that was already done during pre_merge.
11947195464cSYinjun Zhang 	 * The nft entry does not have a chain populated, so
1195a6ffdd3aSLouis Peens 	 * skip this check.
1196a6ffdd3aSLouis Peens 	 */
1197a6ffdd3aSLouis Peens 	err = nfp_ct_merge_check(pre_ct_entry, nft_entry);
1198a6ffdd3aSLouis Peens 	if (err)
1199a6ffdd3aSLouis Peens 		return err;
1200e43d940fSYinjun Zhang 	err = nfp_ct_merge_check(nft_entry, post_ct_entry);
1201a6ffdd3aSLouis Peens 	if (err)
1202a6ffdd3aSLouis Peens 		return err;
1203a6ffdd3aSLouis Peens 	err = nfp_ct_check_meta(post_ct_entry, nft_entry);
1204a6ffdd3aSLouis Peens 	if (err)
1205a6ffdd3aSLouis Peens 		return err;
1206a6ffdd3aSLouis Peens 
1207a87ceb3dSWentao Jia 	if (pre_ct_entry->num_prev_m_entries > 0) {
1208a87ceb3dSWentao Jia 		err = nfp_ct_merge_extra_check(nft_entry, tc_m_entry);
1209a87ceb3dSWentao Jia 		if (err)
1210a87ceb3dSWentao Jia 			return err;
1211a87ceb3dSWentao Jia 	}
1212a87ceb3dSWentao Jia 
1213a6ffdd3aSLouis Peens 	/* Combine tc_merge and nft cookies for this cookie. */
1214a6ffdd3aSLouis Peens 	new_cookie[0] = tc_m_entry->cookie[0];
1215a6ffdd3aSLouis Peens 	new_cookie[1] = tc_m_entry->cookie[1];
1216a6ffdd3aSLouis Peens 	new_cookie[2] = nft_entry->cookie;
1217a6ffdd3aSLouis Peens 	nft_m_entry = get_hashentry(&zt->nft_merge_tb,
1218a6ffdd3aSLouis Peens 				    &new_cookie,
1219a6ffdd3aSLouis Peens 				    nfp_nft_ct_merge_params,
1220a6ffdd3aSLouis Peens 				    sizeof(*nft_m_entry));
1221a6ffdd3aSLouis Peens 
1222a6ffdd3aSLouis Peens 	if (IS_ERR(nft_m_entry))
1223a6ffdd3aSLouis Peens 		return PTR_ERR(nft_m_entry);
1224a6ffdd3aSLouis Peens 
1225a6ffdd3aSLouis Peens 	/* nft_m_entry already present, not merging again */
1226a6ffdd3aSLouis Peens 	if (!memcmp(&new_cookie, nft_m_entry->cookie, sizeof(new_cookie)))
1227a6ffdd3aSLouis Peens 		return 0;
1228a6ffdd3aSLouis Peens 
1229a6ffdd3aSLouis Peens 	memcpy(&nft_m_entry->cookie, &new_cookie, sizeof(new_cookie));
1230a6ffdd3aSLouis Peens 	nft_m_entry->zt = zt;
1231a6ffdd3aSLouis Peens 	nft_m_entry->tc_m_parent = tc_m_entry;
1232a6ffdd3aSLouis Peens 	nft_m_entry->nft_parent = nft_entry;
1233a6ffdd3aSLouis Peens 	nft_m_entry->tc_flower_cookie = 0;
12349bacb93bSWalter Heymans 	/* Copy the netdev from the pre_ct entry. When the tc_m_entry was created
1235a6ffdd3aSLouis Peens 	 * it only combined them if the netdevs were the same, so can use any of them.
1236a6ffdd3aSLouis Peens 	 */
1237a6ffdd3aSLouis Peens 	nft_m_entry->netdev = pre_ct_entry->netdev;
1238a6ffdd3aSLouis Peens 
1239a6ffdd3aSLouis Peens 	/* Add this entry to the tc_m_list and nft_flow lists */
1240a6ffdd3aSLouis Peens 	list_add(&nft_m_entry->tc_merge_list, &tc_m_entry->children);
1241a6ffdd3aSLouis Peens 	list_add(&nft_m_entry->nft_flow_list, &nft_entry->children);
1242a6ffdd3aSLouis Peens 
1243a6ffdd3aSLouis Peens 	err = rhashtable_insert_fast(&zt->nft_merge_tb, &nft_m_entry->hash_node,
1244a6ffdd3aSLouis Peens 				     nfp_nft_ct_merge_params);
1245a6ffdd3aSLouis Peens 	if (err)
1246a6ffdd3aSLouis Peens 		goto err_nft_ct_merge_insert;
1247a6ffdd3aSLouis Peens 
1248a6ffdd3aSLouis Peens 	zt->nft_merge_count++;
1249a6ffdd3aSLouis Peens 
1250a87ceb3dSWentao Jia 	if (post_ct_entry->goto_chain_index > 0)
1251a87ceb3dSWentao Jia 		return nfp_fl_create_new_pre_ct(nft_m_entry);
1252a87ceb3dSWentao Jia 
1253a87ceb3dSWentao Jia 	/* Generate offload structure and send to nfp */
1254a87ceb3dSWentao Jia 	err = nfp_fl_ct_add_offload(nft_m_entry);
1255a87ceb3dSWentao Jia 	if (err)
1256a87ceb3dSWentao Jia 		goto err_nft_ct_offload;
1257a87ceb3dSWentao Jia 
1258a6ffdd3aSLouis Peens 	return err;
1259a6ffdd3aSLouis Peens 
1260a87ceb3dSWentao Jia err_nft_ct_offload:
1261a6ffdd3aSLouis Peens 	nfp_fl_ct_del_offload(zt->priv->app, nft_m_entry->tc_flower_cookie,
1262a6ffdd3aSLouis Peens 			      nft_m_entry->netdev);
1263a87ceb3dSWentao Jia err_nft_ct_merge_insert:
1264a6ffdd3aSLouis Peens 	list_del(&nft_m_entry->tc_merge_list);
1265a6ffdd3aSLouis Peens 	list_del(&nft_m_entry->nft_flow_list);
1266a6ffdd3aSLouis Peens 	kfree(nft_m_entry);
1267a6ffdd3aSLouis Peens 	return err;
1268a6ffdd3aSLouis Peens }
1269a6ffdd3aSLouis Peens 
nfp_ct_do_tc_merge(struct nfp_fl_ct_zone_entry * zt,struct nfp_fl_ct_flow_entry * ct_entry1,struct nfp_fl_ct_flow_entry * ct_entry2)12703c863c30SLouis Peens static int nfp_ct_do_tc_merge(struct nfp_fl_ct_zone_entry *zt,
12713c863c30SLouis Peens 			      struct nfp_fl_ct_flow_entry *ct_entry1,
12723c863c30SLouis Peens 			      struct nfp_fl_ct_flow_entry *ct_entry2)
12733c863c30SLouis Peens {
12743c863c30SLouis Peens 	struct nfp_fl_ct_flow_entry *post_ct_entry, *pre_ct_entry;
1275a6ffdd3aSLouis Peens 	struct nfp_fl_ct_flow_entry *nft_entry, *nft_tmp;
12763c863c30SLouis Peens 	struct nfp_fl_ct_tc_merge *m_entry;
12773c863c30SLouis Peens 	unsigned long new_cookie[2];
12783c863c30SLouis Peens 	int err;
12793c863c30SLouis Peens 
12803c863c30SLouis Peens 	if (ct_entry1->type == CT_TYPE_PRE_CT) {
12813c863c30SLouis Peens 		pre_ct_entry = ct_entry1;
12823c863c30SLouis Peens 		post_ct_entry = ct_entry2;
12833c863c30SLouis Peens 	} else {
12843c863c30SLouis Peens 		post_ct_entry = ct_entry1;
12853c863c30SLouis Peens 		pre_ct_entry = ct_entry2;
12863c863c30SLouis Peens 	}
12873c863c30SLouis Peens 
12883c863c30SLouis Peens 	/* Checks that the chain_index of the filter matches the
12893c863c30SLouis Peens 	 * chain_index of the GOTO action.
12903c863c30SLouis Peens 	 */
12913e44d199SWentao Jia 	if (post_ct_entry->chain_index != pre_ct_entry->goto_chain_index)
12923c863c30SLouis Peens 		return -EINVAL;
12933c863c30SLouis Peens 
1294e43d940fSYinjun Zhang 	err = nfp_ct_merge_check(pre_ct_entry, post_ct_entry);
12953c863c30SLouis Peens 	if (err)
12963c863c30SLouis Peens 		return err;
12973c863c30SLouis Peens 
12983c863c30SLouis Peens 	new_cookie[0] = pre_ct_entry->cookie;
12993c863c30SLouis Peens 	new_cookie[1] = post_ct_entry->cookie;
13003c863c30SLouis Peens 	m_entry = get_hashentry(&zt->tc_merge_tb, &new_cookie,
13013c863c30SLouis Peens 				nfp_tc_ct_merge_params, sizeof(*m_entry));
13023c863c30SLouis Peens 	if (IS_ERR(m_entry))
13033c863c30SLouis Peens 		return PTR_ERR(m_entry);
13043c863c30SLouis Peens 
13053c863c30SLouis Peens 	/* m_entry already present, not merging again */
13063c863c30SLouis Peens 	if (!memcmp(&new_cookie, m_entry->cookie, sizeof(new_cookie)))
13073c863c30SLouis Peens 		return 0;
13083c863c30SLouis Peens 
13093c863c30SLouis Peens 	memcpy(&m_entry->cookie, &new_cookie, sizeof(new_cookie));
13103c863c30SLouis Peens 	m_entry->zt = zt;
13113c863c30SLouis Peens 	m_entry->post_ct_parent = post_ct_entry;
13123c863c30SLouis Peens 	m_entry->pre_ct_parent = pre_ct_entry;
13133c863c30SLouis Peens 
13143c863c30SLouis Peens 	/* Add this entry to the pre_ct and post_ct lists */
13153c863c30SLouis Peens 	list_add(&m_entry->post_ct_list, &post_ct_entry->children);
13163c863c30SLouis Peens 	list_add(&m_entry->pre_ct_list, &pre_ct_entry->children);
13173c863c30SLouis Peens 	INIT_LIST_HEAD(&m_entry->children);
13183c863c30SLouis Peens 
13193c863c30SLouis Peens 	err = rhashtable_insert_fast(&zt->tc_merge_tb, &m_entry->hash_node,
13203c863c30SLouis Peens 				     nfp_tc_ct_merge_params);
13213c863c30SLouis Peens 	if (err)
13223c863c30SLouis Peens 		goto err_ct_tc_merge_insert;
13233c863c30SLouis Peens 	zt->tc_merge_count++;
13243c863c30SLouis Peens 
1325a6ffdd3aSLouis Peens 	/* Merge with existing nft flows */
1326a6ffdd3aSLouis Peens 	list_for_each_entry_safe(nft_entry, nft_tmp, &zt->nft_flows_list,
1327a6ffdd3aSLouis Peens 				 list_node) {
1328a6ffdd3aSLouis Peens 		nfp_ct_do_nft_merge(zt, nft_entry, m_entry);
1329a6ffdd3aSLouis Peens 	}
1330a6ffdd3aSLouis Peens 
13313c863c30SLouis Peens 	return 0;
13323c863c30SLouis Peens 
13333c863c30SLouis Peens err_ct_tc_merge_insert:
13343c863c30SLouis Peens 	list_del(&m_entry->post_ct_list);
13353c863c30SLouis Peens 	list_del(&m_entry->pre_ct_list);
13363c863c30SLouis Peens 	kfree(m_entry);
13373c863c30SLouis Peens 	return err;
13383c863c30SLouis Peens }
13393c863c30SLouis Peens 
1340bd0fe7f9SLouis Peens static struct
get_nfp_zone_entry(struct nfp_flower_priv * priv,u16 zone,bool wildcarded)1341bd0fe7f9SLouis Peens nfp_fl_ct_zone_entry *get_nfp_zone_entry(struct nfp_flower_priv *priv,
1342bd0fe7f9SLouis Peens 					 u16 zone, bool wildcarded)
1343bd0fe7f9SLouis Peens {
1344bd0fe7f9SLouis Peens 	struct nfp_fl_ct_zone_entry *zt;
1345bd0fe7f9SLouis Peens 	int err;
1346bd0fe7f9SLouis Peens 
1347bd0fe7f9SLouis Peens 	if (wildcarded && priv->ct_zone_wc)
1348bd0fe7f9SLouis Peens 		return priv->ct_zone_wc;
1349bd0fe7f9SLouis Peens 
1350bd0fe7f9SLouis Peens 	if (!wildcarded) {
1351bd0fe7f9SLouis Peens 		zt = get_hashentry(&priv->ct_zone_table, &zone,
1352bd0fe7f9SLouis Peens 				   nfp_zone_table_params, sizeof(*zt));
1353bd0fe7f9SLouis Peens 
1354bd0fe7f9SLouis Peens 		/* If priv is set this is an existing entry, just return it */
1355bd0fe7f9SLouis Peens 		if (IS_ERR(zt) || zt->priv)
1356bd0fe7f9SLouis Peens 			return zt;
1357bd0fe7f9SLouis Peens 	} else {
1358bd0fe7f9SLouis Peens 		zt = kzalloc(sizeof(*zt), GFP_KERNEL);
1359bd0fe7f9SLouis Peens 		if (!zt)
1360bd0fe7f9SLouis Peens 			return ERR_PTR(-ENOMEM);
1361bd0fe7f9SLouis Peens 	}
1362bd0fe7f9SLouis Peens 
1363bd0fe7f9SLouis Peens 	zt->zone = zone;
1364bd0fe7f9SLouis Peens 	zt->priv = priv;
1365bd0fe7f9SLouis Peens 	zt->nft = NULL;
1366bd0fe7f9SLouis Peens 
1367072c089cSLouis Peens 	/* init the various hash tables and lists */
1368072c089cSLouis Peens 	INIT_LIST_HEAD(&zt->pre_ct_list);
1369072c089cSLouis Peens 	INIT_LIST_HEAD(&zt->post_ct_list);
137095255017SLouis Peens 	INIT_LIST_HEAD(&zt->nft_flows_list);
1371072c089cSLouis Peens 
1372f7ae12e2SLouis Peens 	err = rhashtable_init(&zt->tc_merge_tb, &nfp_tc_ct_merge_params);
1373f7ae12e2SLouis Peens 	if (err)
1374f7ae12e2SLouis Peens 		goto err_tc_merge_tb_init;
1375f7ae12e2SLouis Peens 
1376b5e30c61SLouis Peens 	err = rhashtable_init(&zt->nft_merge_tb, &nfp_nft_ct_merge_params);
1377b5e30c61SLouis Peens 	if (err)
1378b5e30c61SLouis Peens 		goto err_nft_merge_tb_init;
1379b5e30c61SLouis Peens 
1380bd0fe7f9SLouis Peens 	if (wildcarded) {
1381bd0fe7f9SLouis Peens 		priv->ct_zone_wc = zt;
1382bd0fe7f9SLouis Peens 	} else {
1383bd0fe7f9SLouis Peens 		err = rhashtable_insert_fast(&priv->ct_zone_table,
1384bd0fe7f9SLouis Peens 					     &zt->hash_node,
1385bd0fe7f9SLouis Peens 					     nfp_zone_table_params);
1386bd0fe7f9SLouis Peens 		if (err)
1387bd0fe7f9SLouis Peens 			goto err_zone_insert;
1388bd0fe7f9SLouis Peens 	}
1389bd0fe7f9SLouis Peens 
1390bd0fe7f9SLouis Peens 	return zt;
1391bd0fe7f9SLouis Peens 
1392bd0fe7f9SLouis Peens err_zone_insert:
1393b5e30c61SLouis Peens 	rhashtable_destroy(&zt->nft_merge_tb);
1394b5e30c61SLouis Peens err_nft_merge_tb_init:
1395f7ae12e2SLouis Peens 	rhashtable_destroy(&zt->tc_merge_tb);
1396f7ae12e2SLouis Peens err_tc_merge_tb_init:
1397bd0fe7f9SLouis Peens 	kfree(zt);
1398bd0fe7f9SLouis Peens 	return ERR_PTR(err);
1399bd0fe7f9SLouis Peens }
1400bd0fe7f9SLouis Peens 
get_netdev_from_rule(struct flow_rule * rule)14017195464cSYinjun Zhang static struct net_device *get_netdev_from_rule(struct flow_rule *rule)
14027195464cSYinjun Zhang {
14037195464cSYinjun Zhang 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META)) {
14047195464cSYinjun Zhang 		struct flow_match_meta match;
14057195464cSYinjun Zhang 
14067195464cSYinjun Zhang 		flow_rule_match_meta(rule, &match);
14077195464cSYinjun Zhang 		if (match.key->ingress_ifindex & match.mask->ingress_ifindex)
14087195464cSYinjun Zhang 			return __dev_get_by_index(&init_net,
14097195464cSYinjun Zhang 						  match.key->ingress_ifindex);
14107195464cSYinjun Zhang 	}
14117195464cSYinjun Zhang 
14127195464cSYinjun Zhang 	return NULL;
14137195464cSYinjun Zhang }
14147195464cSYinjun Zhang 
nfp_nft_ct_translate_mangle_action(struct flow_action_entry * mangle_action)14155cee92c6SHui Zhou static void nfp_nft_ct_translate_mangle_action(struct flow_action_entry *mangle_action)
14165cee92c6SHui Zhou {
14175cee92c6SHui Zhou 	if (mangle_action->id != FLOW_ACTION_MANGLE)
14185cee92c6SHui Zhou 		return;
14195cee92c6SHui Zhou 
14205cee92c6SHui Zhou 	switch (mangle_action->mangle.htype) {
14215cee92c6SHui Zhou 	case FLOW_ACT_MANGLE_HDR_TYPE_IP4:
14225cee92c6SHui Zhou 	case FLOW_ACT_MANGLE_HDR_TYPE_IP6:
14235cee92c6SHui Zhou 		mangle_action->mangle.val = (__force u32)cpu_to_be32(mangle_action->mangle.val);
14245cee92c6SHui Zhou 		mangle_action->mangle.mask = (__force u32)cpu_to_be32(mangle_action->mangle.mask);
14255cee92c6SHui Zhou 		return;
14265cee92c6SHui Zhou 
1427*86635641SHui Zhou 	/* Both struct tcphdr and struct udphdr start with
1428*86635641SHui Zhou 	 *	__be16 source;
1429*86635641SHui Zhou 	 *	__be16 dest;
1430*86635641SHui Zhou 	 * so we can use the same code for both.
1431*86635641SHui Zhou 	 */
14325cee92c6SHui Zhou 	case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
14335cee92c6SHui Zhou 	case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
1434*86635641SHui Zhou 		if (mangle_action->mangle.offset == offsetof(struct tcphdr, source)) {
1435*86635641SHui Zhou 			mangle_action->mangle.val =
1436*86635641SHui Zhou 				(__force u32)cpu_to_be32(mangle_action->mangle.val << 16);
1437*86635641SHui Zhou 			/* The mask of mangle action is inverse mask,
1438*86635641SHui Zhou 			 * so clear the dest tp port with 0xFFFF to
1439*86635641SHui Zhou 			 * instead of rotate-left operation.
1440*86635641SHui Zhou 			 */
1441*86635641SHui Zhou 			mangle_action->mangle.mask =
1442*86635641SHui Zhou 				(__force u32)cpu_to_be32(mangle_action->mangle.mask << 16 | 0xFFFF);
1443*86635641SHui Zhou 		}
1444*86635641SHui Zhou 		if (mangle_action->mangle.offset == offsetof(struct tcphdr, dest)) {
1445*86635641SHui Zhou 			mangle_action->mangle.offset = 0;
1446*86635641SHui Zhou 			mangle_action->mangle.val =
1447*86635641SHui Zhou 				(__force u32)cpu_to_be32(mangle_action->mangle.val);
1448*86635641SHui Zhou 			mangle_action->mangle.mask =
1449*86635641SHui Zhou 				(__force u32)cpu_to_be32(mangle_action->mangle.mask);
1450*86635641SHui Zhou 		}
14515cee92c6SHui Zhou 		return;
14525cee92c6SHui Zhou 
14535cee92c6SHui Zhou 	default:
14545cee92c6SHui Zhou 		return;
14555cee92c6SHui Zhou 	}
14565cee92c6SHui Zhou }
14575cee92c6SHui Zhou 
nfp_nft_ct_set_flow_flag(struct flow_action_entry * act,struct nfp_fl_ct_flow_entry * entry)14585cee92c6SHui Zhou static int nfp_nft_ct_set_flow_flag(struct flow_action_entry *act,
14595cee92c6SHui Zhou 				    struct nfp_fl_ct_flow_entry *entry)
14605cee92c6SHui Zhou {
14615cee92c6SHui Zhou 	switch (act->id) {
14625cee92c6SHui Zhou 	case FLOW_ACTION_CT:
14635cee92c6SHui Zhou 		if (act->ct.action == TCA_CT_ACT_NAT)
14645cee92c6SHui Zhou 			entry->flags |= NFP_FL_ACTION_DO_NAT;
14655cee92c6SHui Zhou 		break;
14665cee92c6SHui Zhou 
14675cee92c6SHui Zhou 	case FLOW_ACTION_MANGLE:
14685cee92c6SHui Zhou 		entry->flags |= NFP_FL_ACTION_DO_MANGLE;
14695cee92c6SHui Zhou 		break;
14705cee92c6SHui Zhou 
14715cee92c6SHui Zhou 	default:
14725cee92c6SHui Zhou 		break;
14735cee92c6SHui Zhou 	}
14745cee92c6SHui Zhou 
14755cee92c6SHui Zhou 	return 0;
14765cee92c6SHui Zhou }
14775cee92c6SHui Zhou 
1478072c089cSLouis Peens static struct
nfp_fl_ct_add_flow(struct nfp_fl_ct_zone_entry * zt,struct net_device * netdev,struct flow_cls_offload * flow,bool is_nft,struct netlink_ext_ack * extack)1479072c089cSLouis Peens nfp_fl_ct_flow_entry *nfp_fl_ct_add_flow(struct nfp_fl_ct_zone_entry *zt,
1480072c089cSLouis Peens 					 struct net_device *netdev,
1481fa81d6d2SLouis Peens 					 struct flow_cls_offload *flow,
14824772ad3fSYinjun Zhang 					 bool is_nft, struct netlink_ext_ack *extack)
1483072c089cSLouis Peens {
14844772ad3fSYinjun Zhang 	struct nf_flow_match *nft_match = NULL;
1485072c089cSLouis Peens 	struct nfp_fl_ct_flow_entry *entry;
1486fa81d6d2SLouis Peens 	struct nfp_fl_ct_map_entry *map;
1487072c089cSLouis Peens 	struct flow_action_entry *act;
1488072c089cSLouis Peens 	int err, i;
1489072c089cSLouis Peens 
1490072c089cSLouis Peens 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
1491072c089cSLouis Peens 	if (!entry)
1492072c089cSLouis Peens 		return ERR_PTR(-ENOMEM);
1493072c089cSLouis Peens 
1494072c089cSLouis Peens 	entry->rule = flow_rule_alloc(flow->rule->action.num_entries);
1495072c089cSLouis Peens 	if (!entry->rule) {
1496072c089cSLouis Peens 		err = -ENOMEM;
14974772ad3fSYinjun Zhang 		goto err_pre_ct_rule;
14984772ad3fSYinjun Zhang 	}
14994772ad3fSYinjun Zhang 
15004772ad3fSYinjun Zhang 	/* nft flows gets destroyed after callback return, so need
15014772ad3fSYinjun Zhang 	 * to do a full copy instead of just a reference.
15024772ad3fSYinjun Zhang 	 */
15034772ad3fSYinjun Zhang 	if (is_nft) {
15044772ad3fSYinjun Zhang 		nft_match = kzalloc(sizeof(*nft_match), GFP_KERNEL);
15054772ad3fSYinjun Zhang 		if (!nft_match) {
15064772ad3fSYinjun Zhang 			err = -ENOMEM;
1507072c089cSLouis Peens 			goto err_pre_ct_act;
1508072c089cSLouis Peens 		}
15094772ad3fSYinjun Zhang 		memcpy(&nft_match->dissector, flow->rule->match.dissector,
15104772ad3fSYinjun Zhang 		       sizeof(nft_match->dissector));
15114772ad3fSYinjun Zhang 		memcpy(&nft_match->mask, flow->rule->match.mask,
15124772ad3fSYinjun Zhang 		       sizeof(nft_match->mask));
15134772ad3fSYinjun Zhang 		memcpy(&nft_match->key, flow->rule->match.key,
15144772ad3fSYinjun Zhang 		       sizeof(nft_match->key));
15154772ad3fSYinjun Zhang 		entry->rule->match.dissector = &nft_match->dissector;
15164772ad3fSYinjun Zhang 		entry->rule->match.mask = &nft_match->mask;
15174772ad3fSYinjun Zhang 		entry->rule->match.key = &nft_match->key;
15187195464cSYinjun Zhang 
15197195464cSYinjun Zhang 		if (!netdev)
15207195464cSYinjun Zhang 			netdev = get_netdev_from_rule(entry->rule);
15214772ad3fSYinjun Zhang 	} else {
1522072c089cSLouis Peens 		entry->rule->match.dissector = flow->rule->match.dissector;
1523072c089cSLouis Peens 		entry->rule->match.mask = flow->rule->match.mask;
1524072c089cSLouis Peens 		entry->rule->match.key = flow->rule->match.key;
15254772ad3fSYinjun Zhang 	}
15264772ad3fSYinjun Zhang 
15274772ad3fSYinjun Zhang 	entry->zt = zt;
15284772ad3fSYinjun Zhang 	entry->netdev = netdev;
1529a87ceb3dSWentao Jia 	entry->cookie = flow->cookie > 0 ? flow->cookie : (unsigned long)entry;
1530072c089cSLouis Peens 	entry->chain_index = flow->common.chain_index;
1531072c089cSLouis Peens 	entry->tun_offset = NFP_FL_CT_NO_TUN;
1532072c089cSLouis Peens 
1533072c089cSLouis Peens 	/* Copy over action data. Unfortunately we do not get a handle to the
1534072c089cSLouis Peens 	 * original tcf_action data, and the flow objects gets destroyed, so we
1535072c089cSLouis Peens 	 * cannot just save a pointer to this either, so need to copy over the
1536072c089cSLouis Peens 	 * data unfortunately.
1537072c089cSLouis Peens 	 */
1538072c089cSLouis Peens 	entry->rule->action.num_entries = flow->rule->action.num_entries;
1539072c089cSLouis Peens 	flow_action_for_each(i, act, &flow->rule->action) {
1540072c089cSLouis Peens 		struct flow_action_entry *new_act;
1541072c089cSLouis Peens 
1542072c089cSLouis Peens 		new_act = &entry->rule->action.entries[i];
1543072c089cSLouis Peens 		memcpy(new_act, act, sizeof(struct flow_action_entry));
15445cee92c6SHui Zhou 		/* nft entry mangle field is host byte order, need translate to
15455cee92c6SHui Zhou 		 * network byte order.
15465cee92c6SHui Zhou 		 */
15475cee92c6SHui Zhou 		if (is_nft)
15485cee92c6SHui Zhou 			nfp_nft_ct_translate_mangle_action(new_act);
15495cee92c6SHui Zhou 
15505cee92c6SHui Zhou 		nfp_nft_ct_set_flow_flag(new_act, entry);
1551072c089cSLouis Peens 		/* Entunnel is a special case, need to allocate and copy
1552072c089cSLouis Peens 		 * tunnel info.
1553072c089cSLouis Peens 		 */
1554072c089cSLouis Peens 		if (act->id == FLOW_ACTION_TUNNEL_ENCAP) {
1555072c089cSLouis Peens 			struct ip_tunnel_info *tun = act->tunnel;
1556072c089cSLouis Peens 			size_t tun_size = sizeof(*tun) + tun->options_len;
1557072c089cSLouis Peens 
1558072c089cSLouis Peens 			new_act->tunnel = kmemdup(tun, tun_size, GFP_ATOMIC);
1559072c089cSLouis Peens 			if (!new_act->tunnel) {
1560072c089cSLouis Peens 				err = -ENOMEM;
1561072c089cSLouis Peens 				goto err_pre_ct_tun_cp;
1562072c089cSLouis Peens 			}
1563072c089cSLouis Peens 			entry->tun_offset = i;
1564072c089cSLouis Peens 		}
1565072c089cSLouis Peens 	}
1566072c089cSLouis Peens 
1567072c089cSLouis Peens 	INIT_LIST_HEAD(&entry->children);
1568072c089cSLouis Peens 
1569a87ceb3dSWentao Jia 	if (flow->cookie == 0)
1570a87ceb3dSWentao Jia 		return entry;
1571a87ceb3dSWentao Jia 
1572fa81d6d2SLouis Peens 	/* Now add a ct map entry to flower-priv */
1573fa81d6d2SLouis Peens 	map = get_hashentry(&zt->priv->ct_map_table, &flow->cookie,
1574fa81d6d2SLouis Peens 			    nfp_ct_map_params, sizeof(*map));
1575fa81d6d2SLouis Peens 	if (IS_ERR(map)) {
1576fa81d6d2SLouis Peens 		NL_SET_ERR_MSG_MOD(extack,
1577fa81d6d2SLouis Peens 				   "offload error: ct map entry creation failed");
1578fa81d6d2SLouis Peens 		err = -ENOMEM;
1579fa81d6d2SLouis Peens 		goto err_ct_flow_insert;
1580fa81d6d2SLouis Peens 	}
1581fa81d6d2SLouis Peens 	map->cookie = flow->cookie;
1582fa81d6d2SLouis Peens 	map->ct_entry = entry;
1583fa81d6d2SLouis Peens 	err = rhashtable_insert_fast(&zt->priv->ct_map_table,
1584fa81d6d2SLouis Peens 				     &map->hash_node,
1585fa81d6d2SLouis Peens 				     nfp_ct_map_params);
1586fa81d6d2SLouis Peens 	if (err) {
1587fa81d6d2SLouis Peens 		NL_SET_ERR_MSG_MOD(extack,
1588fa81d6d2SLouis Peens 				   "offload error: ct map entry table add failed");
1589fa81d6d2SLouis Peens 		goto err_map_insert;
1590fa81d6d2SLouis Peens 	}
1591072c089cSLouis Peens 
1592072c089cSLouis Peens 	return entry;
1593072c089cSLouis Peens 
1594fa81d6d2SLouis Peens err_map_insert:
1595fa81d6d2SLouis Peens 	kfree(map);
1596fa81d6d2SLouis Peens err_ct_flow_insert:
1597fa81d6d2SLouis Peens 	if (entry->tun_offset != NFP_FL_CT_NO_TUN)
1598fa81d6d2SLouis Peens 		kfree(entry->rule->action.entries[entry->tun_offset].tunnel);
1599072c089cSLouis Peens err_pre_ct_tun_cp:
16004772ad3fSYinjun Zhang 	kfree(nft_match);
1601072c089cSLouis Peens err_pre_ct_act:
16024772ad3fSYinjun Zhang 	kfree(entry->rule);
16034772ad3fSYinjun Zhang err_pre_ct_rule:
1604072c089cSLouis Peens 	kfree(entry);
1605072c089cSLouis Peens 	return ERR_PTR(err);
1606072c089cSLouis Peens }
1607072c089cSLouis Peens 
cleanup_nft_merge_entry(struct nfp_fl_nft_tc_merge * m_entry)1608a6ffdd3aSLouis Peens static void cleanup_nft_merge_entry(struct nfp_fl_nft_tc_merge *m_entry)
1609a6ffdd3aSLouis Peens {
1610a6ffdd3aSLouis Peens 	struct nfp_fl_ct_zone_entry *zt;
1611a6ffdd3aSLouis Peens 	int err;
1612a6ffdd3aSLouis Peens 
1613a6ffdd3aSLouis Peens 	zt = m_entry->zt;
1614a6ffdd3aSLouis Peens 
1615a6ffdd3aSLouis Peens 	/* Flow is in HW, need to delete */
1616a6ffdd3aSLouis Peens 	if (m_entry->tc_flower_cookie) {
1617a6ffdd3aSLouis Peens 		err = nfp_fl_ct_del_offload(zt->priv->app, m_entry->tc_flower_cookie,
1618a6ffdd3aSLouis Peens 					    m_entry->netdev);
1619a6ffdd3aSLouis Peens 		if (err)
1620a6ffdd3aSLouis Peens 			return;
1621a6ffdd3aSLouis Peens 	}
1622a6ffdd3aSLouis Peens 
1623a6ffdd3aSLouis Peens 	WARN_ON_ONCE(rhashtable_remove_fast(&zt->nft_merge_tb,
1624a6ffdd3aSLouis Peens 					    &m_entry->hash_node,
1625a6ffdd3aSLouis Peens 					    nfp_nft_ct_merge_params));
1626a6ffdd3aSLouis Peens 	zt->nft_merge_count--;
1627a6ffdd3aSLouis Peens 	list_del(&m_entry->tc_merge_list);
1628a6ffdd3aSLouis Peens 	list_del(&m_entry->nft_flow_list);
1629a6ffdd3aSLouis Peens 
1630a87ceb3dSWentao Jia 	if (m_entry->next_pre_ct_entry) {
1631a87ceb3dSWentao Jia 		struct nfp_fl_ct_map_entry pre_ct_map_ent;
1632a87ceb3dSWentao Jia 
1633a87ceb3dSWentao Jia 		pre_ct_map_ent.ct_entry = m_entry->next_pre_ct_entry;
1634a87ceb3dSWentao Jia 		pre_ct_map_ent.cookie = 0;
1635a87ceb3dSWentao Jia 		nfp_fl_ct_del_flow(&pre_ct_map_ent);
1636a87ceb3dSWentao Jia 	}
1637a87ceb3dSWentao Jia 
1638a6ffdd3aSLouis Peens 	kfree(m_entry);
1639a6ffdd3aSLouis Peens }
1640a6ffdd3aSLouis Peens 
nfp_free_nft_merge_children(void * entry,bool is_nft_flow)16413c863c30SLouis Peens static void nfp_free_nft_merge_children(void *entry, bool is_nft_flow)
1642072c089cSLouis Peens {
1643a6ffdd3aSLouis Peens 	struct nfp_fl_nft_tc_merge *m_entry, *tmp;
1644a6ffdd3aSLouis Peens 
1645a6ffdd3aSLouis Peens 	/* These post entries are parts of two lists, one is a list of nft_entries
1646a6ffdd3aSLouis Peens 	 * and the other is of from a list of tc_merge structures. Iterate
1647a6ffdd3aSLouis Peens 	 * through the relevant list and cleanup the entries.
1648a6ffdd3aSLouis Peens 	 */
1649a6ffdd3aSLouis Peens 
1650a6ffdd3aSLouis Peens 	if (is_nft_flow) {
1651a6ffdd3aSLouis Peens 		/* Need to iterate through list of nft_flow entries */
1652a6ffdd3aSLouis Peens 		struct nfp_fl_ct_flow_entry *ct_entry = entry;
1653a6ffdd3aSLouis Peens 
1654a6ffdd3aSLouis Peens 		list_for_each_entry_safe(m_entry, tmp, &ct_entry->children,
1655a6ffdd3aSLouis Peens 					 nft_flow_list) {
1656a6ffdd3aSLouis Peens 			cleanup_nft_merge_entry(m_entry);
1657a6ffdd3aSLouis Peens 		}
1658a6ffdd3aSLouis Peens 	} else {
1659a6ffdd3aSLouis Peens 		/* Need to iterate through list of tc_merged_flow entries */
1660a6ffdd3aSLouis Peens 		struct nfp_fl_ct_tc_merge *ct_entry = entry;
1661a6ffdd3aSLouis Peens 
1662a6ffdd3aSLouis Peens 		list_for_each_entry_safe(m_entry, tmp, &ct_entry->children,
1663a6ffdd3aSLouis Peens 					 tc_merge_list) {
1664a6ffdd3aSLouis Peens 			cleanup_nft_merge_entry(m_entry);
1665a6ffdd3aSLouis Peens 		}
1666a6ffdd3aSLouis Peens 	}
1667072c089cSLouis Peens }
1668072c089cSLouis Peens 
nfp_del_tc_merge_entry(struct nfp_fl_ct_tc_merge * m_ent)16693c863c30SLouis Peens static void nfp_del_tc_merge_entry(struct nfp_fl_ct_tc_merge *m_ent)
1670072c089cSLouis Peens {
16713c863c30SLouis Peens 	struct nfp_fl_ct_zone_entry *zt;
16723c863c30SLouis Peens 	int err;
16733c863c30SLouis Peens 
16743c863c30SLouis Peens 	zt = m_ent->zt;
16753c863c30SLouis Peens 	err = rhashtable_remove_fast(&zt->tc_merge_tb,
16763c863c30SLouis Peens 				     &m_ent->hash_node,
16773c863c30SLouis Peens 				     nfp_tc_ct_merge_params);
16783c863c30SLouis Peens 	if (err)
16793c863c30SLouis Peens 		pr_warn("WARNING: could not remove merge_entry from hashtable\n");
16803c863c30SLouis Peens 	zt->tc_merge_count--;
16813c863c30SLouis Peens 	list_del(&m_ent->post_ct_list);
16823c863c30SLouis Peens 	list_del(&m_ent->pre_ct_list);
16833c863c30SLouis Peens 
16843c863c30SLouis Peens 	if (!list_empty(&m_ent->children))
16853c863c30SLouis Peens 		nfp_free_nft_merge_children(m_ent, false);
16863c863c30SLouis Peens 	kfree(m_ent);
16873c863c30SLouis Peens }
16883c863c30SLouis Peens 
nfp_free_tc_merge_children(struct nfp_fl_ct_flow_entry * entry)16893c863c30SLouis Peens static void nfp_free_tc_merge_children(struct nfp_fl_ct_flow_entry *entry)
16903c863c30SLouis Peens {
16913c863c30SLouis Peens 	struct nfp_fl_ct_tc_merge *m_ent, *tmp;
16923c863c30SLouis Peens 
16933c863c30SLouis Peens 	switch (entry->type) {
16943c863c30SLouis Peens 	case CT_TYPE_PRE_CT:
16953c863c30SLouis Peens 		list_for_each_entry_safe(m_ent, tmp, &entry->children, pre_ct_list) {
16963c863c30SLouis Peens 			nfp_del_tc_merge_entry(m_ent);
16973c863c30SLouis Peens 		}
16983c863c30SLouis Peens 		break;
16993c863c30SLouis Peens 	case CT_TYPE_POST_CT:
17003c863c30SLouis Peens 		list_for_each_entry_safe(m_ent, tmp, &entry->children, post_ct_list) {
17013c863c30SLouis Peens 			nfp_del_tc_merge_entry(m_ent);
17023c863c30SLouis Peens 		}
17033c863c30SLouis Peens 		break;
17043c863c30SLouis Peens 	default:
17053c863c30SLouis Peens 		break;
17063c863c30SLouis Peens 	}
1707072c089cSLouis Peens }
1708072c089cSLouis Peens 
nfp_fl_ct_clean_flow_entry(struct nfp_fl_ct_flow_entry * entry)1709072c089cSLouis Peens void nfp_fl_ct_clean_flow_entry(struct nfp_fl_ct_flow_entry *entry)
1710072c089cSLouis Peens {
1711072c089cSLouis Peens 	list_del(&entry->list_node);
1712072c089cSLouis Peens 
1713072c089cSLouis Peens 	if (!list_empty(&entry->children)) {
1714072c089cSLouis Peens 		if (entry->type == CT_TYPE_NFT)
1715072c089cSLouis Peens 			nfp_free_nft_merge_children(entry, true);
1716072c089cSLouis Peens 		else
1717072c089cSLouis Peens 			nfp_free_tc_merge_children(entry);
1718072c089cSLouis Peens 	}
1719072c089cSLouis Peens 
1720072c089cSLouis Peens 	if (entry->tun_offset != NFP_FL_CT_NO_TUN)
1721072c089cSLouis Peens 		kfree(entry->rule->action.entries[entry->tun_offset].tunnel);
17224772ad3fSYinjun Zhang 
17234772ad3fSYinjun Zhang 	if (entry->type == CT_TYPE_NFT) {
17244772ad3fSYinjun Zhang 		struct nf_flow_match *nft_match;
17254772ad3fSYinjun Zhang 
17264772ad3fSYinjun Zhang 		nft_match = container_of(entry->rule->match.dissector,
17274772ad3fSYinjun Zhang 					 struct nf_flow_match, dissector);
17284772ad3fSYinjun Zhang 		kfree(nft_match);
17294772ad3fSYinjun Zhang 	}
17304772ad3fSYinjun Zhang 
1731072c089cSLouis Peens 	kfree(entry->rule);
1732072c089cSLouis Peens 	kfree(entry);
1733072c089cSLouis Peens }
1734072c089cSLouis Peens 
get_flow_act_ct(struct flow_rule * rule)17358a8db7aeSWentao Jia static struct flow_action_entry *get_flow_act_ct(struct flow_rule *rule)
17368a8db7aeSWentao Jia {
17378a8db7aeSWentao Jia 	struct flow_action_entry *act;
17388a8db7aeSWentao Jia 	int i;
17398a8db7aeSWentao Jia 
17408a8db7aeSWentao Jia 	/* More than one ct action may be present in a flow rule,
17418a8db7aeSWentao Jia 	 * Return the first one that is not a CT clear action
17428a8db7aeSWentao Jia 	 */
17438a8db7aeSWentao Jia 	flow_action_for_each(i, act, &rule->action) {
17448a8db7aeSWentao Jia 		if (act->id == FLOW_ACTION_CT && act->ct.action != TCA_CT_ACT_CLEAR)
17458a8db7aeSWentao Jia 			return act;
17468a8db7aeSWentao Jia 	}
17478a8db7aeSWentao Jia 
17488a8db7aeSWentao Jia 	return NULL;
17498a8db7aeSWentao Jia }
17508a8db7aeSWentao Jia 
get_flow_act(struct flow_rule * rule,enum flow_action_id act_id)17515e5f0816SLouis Peens static struct flow_action_entry *get_flow_act(struct flow_rule *rule,
1752bd0fe7f9SLouis Peens 					      enum flow_action_id act_id)
1753bd0fe7f9SLouis Peens {
1754bd0fe7f9SLouis Peens 	struct flow_action_entry *act = NULL;
1755bd0fe7f9SLouis Peens 	int i;
1756bd0fe7f9SLouis Peens 
17575e5f0816SLouis Peens 	flow_action_for_each(i, act, &rule->action) {
1758bd0fe7f9SLouis Peens 		if (act->id == act_id)
1759bd0fe7f9SLouis Peens 			return act;
1760bd0fe7f9SLouis Peens 	}
1761bd0fe7f9SLouis Peens 	return NULL;
1762bd0fe7f9SLouis Peens }
1763bd0fe7f9SLouis Peens 
17643c863c30SLouis Peens static void
nfp_ct_merge_tc_entries(struct nfp_fl_ct_flow_entry * ct_entry1,struct nfp_fl_ct_zone_entry * zt_src,struct nfp_fl_ct_zone_entry * zt_dst)17653c863c30SLouis Peens nfp_ct_merge_tc_entries(struct nfp_fl_ct_flow_entry *ct_entry1,
17663c863c30SLouis Peens 			struct nfp_fl_ct_zone_entry *zt_src,
17673c863c30SLouis Peens 			struct nfp_fl_ct_zone_entry *zt_dst)
17683c863c30SLouis Peens {
17693c863c30SLouis Peens 	struct nfp_fl_ct_flow_entry *ct_entry2, *ct_tmp;
17703c863c30SLouis Peens 	struct list_head *ct_list;
17713c863c30SLouis Peens 
17723c863c30SLouis Peens 	if (ct_entry1->type == CT_TYPE_PRE_CT)
17733c863c30SLouis Peens 		ct_list = &zt_src->post_ct_list;
17743c863c30SLouis Peens 	else if (ct_entry1->type == CT_TYPE_POST_CT)
17753c863c30SLouis Peens 		ct_list = &zt_src->pre_ct_list;
17763c863c30SLouis Peens 	else
17773c863c30SLouis Peens 		return;
17783c863c30SLouis Peens 
17793c863c30SLouis Peens 	list_for_each_entry_safe(ct_entry2, ct_tmp, ct_list,
17803c863c30SLouis Peens 				 list_node) {
17813c863c30SLouis Peens 		nfp_ct_do_tc_merge(zt_dst, ct_entry2, ct_entry1);
17823c863c30SLouis Peens 	}
17833c863c30SLouis Peens }
17843c863c30SLouis Peens 
1785a6ffdd3aSLouis Peens static void
nfp_ct_merge_nft_with_tc(struct nfp_fl_ct_flow_entry * nft_entry,struct nfp_fl_ct_zone_entry * zt)1786a6ffdd3aSLouis Peens nfp_ct_merge_nft_with_tc(struct nfp_fl_ct_flow_entry *nft_entry,
1787a6ffdd3aSLouis Peens 			 struct nfp_fl_ct_zone_entry *zt)
1788a6ffdd3aSLouis Peens {
1789a6ffdd3aSLouis Peens 	struct nfp_fl_ct_tc_merge *tc_merge_entry;
1790a6ffdd3aSLouis Peens 	struct rhashtable_iter iter;
1791a6ffdd3aSLouis Peens 
1792a6ffdd3aSLouis Peens 	rhashtable_walk_enter(&zt->tc_merge_tb, &iter);
1793a6ffdd3aSLouis Peens 	rhashtable_walk_start(&iter);
1794a6ffdd3aSLouis Peens 	while ((tc_merge_entry = rhashtable_walk_next(&iter)) != NULL) {
1795a6ffdd3aSLouis Peens 		if (IS_ERR(tc_merge_entry))
1796a6ffdd3aSLouis Peens 			continue;
1797a6ffdd3aSLouis Peens 		rhashtable_walk_stop(&iter);
1798a6ffdd3aSLouis Peens 		nfp_ct_do_nft_merge(zt, nft_entry, tc_merge_entry);
1799a6ffdd3aSLouis Peens 		rhashtable_walk_start(&iter);
1800a6ffdd3aSLouis Peens 	}
1801a6ffdd3aSLouis Peens 	rhashtable_walk_stop(&iter);
1802a6ffdd3aSLouis Peens 	rhashtable_walk_exit(&iter);
1803a6ffdd3aSLouis Peens }
1804a6ffdd3aSLouis Peens 
nfp_fl_ct_handle_pre_ct(struct nfp_flower_priv * priv,struct net_device * netdev,struct flow_cls_offload * flow,struct netlink_ext_ack * extack,struct nfp_fl_nft_tc_merge * m_entry)1805c8b034fbSLouis Peens int nfp_fl_ct_handle_pre_ct(struct nfp_flower_priv *priv,
1806c8b034fbSLouis Peens 			    struct net_device *netdev,
1807c8b034fbSLouis Peens 			    struct flow_cls_offload *flow,
1808a87ceb3dSWentao Jia 			    struct netlink_ext_ack *extack,
1809a87ceb3dSWentao Jia 			    struct nfp_fl_nft_tc_merge *m_entry)
1810c8b034fbSLouis Peens {
1811072c089cSLouis Peens 	struct flow_action_entry *ct_act, *ct_goto;
1812072c089cSLouis Peens 	struct nfp_fl_ct_flow_entry *ct_entry;
1813bd0fe7f9SLouis Peens 	struct nfp_fl_ct_zone_entry *zt;
181462268e78SLouis Peens 	int err;
1815bd0fe7f9SLouis Peens 
18168a8db7aeSWentao Jia 	ct_act = get_flow_act_ct(flow->rule);
1817bd0fe7f9SLouis Peens 	if (!ct_act) {
1818bd0fe7f9SLouis Peens 		NL_SET_ERR_MSG_MOD(extack,
1819bd0fe7f9SLouis Peens 				   "unsupported offload: Conntrack action empty in conntrack offload");
1820bd0fe7f9SLouis Peens 		return -EOPNOTSUPP;
1821bd0fe7f9SLouis Peens 	}
1822bd0fe7f9SLouis Peens 
18235e5f0816SLouis Peens 	ct_goto = get_flow_act(flow->rule, FLOW_ACTION_GOTO);
1824072c089cSLouis Peens 	if (!ct_goto) {
1825072c089cSLouis Peens 		NL_SET_ERR_MSG_MOD(extack,
1826072c089cSLouis Peens 				   "unsupported offload: Conntrack requires ACTION_GOTO");
1827072c089cSLouis Peens 		return -EOPNOTSUPP;
1828072c089cSLouis Peens 	}
1829072c089cSLouis Peens 
1830bd0fe7f9SLouis Peens 	zt = get_nfp_zone_entry(priv, ct_act->ct.zone, false);
1831bd0fe7f9SLouis Peens 	if (IS_ERR(zt)) {
1832bd0fe7f9SLouis Peens 		NL_SET_ERR_MSG_MOD(extack,
1833bd0fe7f9SLouis Peens 				   "offload error: Could not create zone table entry");
1834bd0fe7f9SLouis Peens 		return PTR_ERR(zt);
1835bd0fe7f9SLouis Peens 	}
1836bd0fe7f9SLouis Peens 
183762268e78SLouis Peens 	if (!zt->nft) {
1838bd0fe7f9SLouis Peens 		zt->nft = ct_act->ct.flow_table;
183962268e78SLouis Peens 		err = nf_flow_table_offload_add_cb(zt->nft, nfp_fl_ct_handle_nft_flow, zt);
184062268e78SLouis Peens 		if (err) {
184162268e78SLouis Peens 			NL_SET_ERR_MSG_MOD(extack,
184262268e78SLouis Peens 					   "offload error: Could not register nft_callback");
184362268e78SLouis Peens 			return err;
184462268e78SLouis Peens 		}
184562268e78SLouis Peens 	}
1846bd0fe7f9SLouis Peens 
1847072c089cSLouis Peens 	/* Add entry to pre_ct_list */
18484772ad3fSYinjun Zhang 	ct_entry = nfp_fl_ct_add_flow(zt, netdev, flow, false, extack);
1849072c089cSLouis Peens 	if (IS_ERR(ct_entry))
1850072c089cSLouis Peens 		return PTR_ERR(ct_entry);
1851072c089cSLouis Peens 	ct_entry->type = CT_TYPE_PRE_CT;
18523e44d199SWentao Jia 	ct_entry->chain_index = flow->common.chain_index;
18533e44d199SWentao Jia 	ct_entry->goto_chain_index = ct_goto->chain_index;
1854a87ceb3dSWentao Jia 
1855a87ceb3dSWentao Jia 	if (m_entry) {
1856a87ceb3dSWentao Jia 		struct nfp_fl_ct_flow_entry *pre_ct_entry;
1857a87ceb3dSWentao Jia 		int i;
1858a87ceb3dSWentao Jia 
1859a87ceb3dSWentao Jia 		pre_ct_entry = m_entry->tc_m_parent->pre_ct_parent;
1860a87ceb3dSWentao Jia 		for (i = 0; i < pre_ct_entry->num_prev_m_entries; i++)
1861a87ceb3dSWentao Jia 			ct_entry->prev_m_entries[i] = pre_ct_entry->prev_m_entries[i];
1862a87ceb3dSWentao Jia 		ct_entry->prev_m_entries[i++] = m_entry;
1863a87ceb3dSWentao Jia 		ct_entry->num_prev_m_entries = i;
1864a87ceb3dSWentao Jia 
1865a87ceb3dSWentao Jia 		m_entry->next_pre_ct_entry = ct_entry;
1866a87ceb3dSWentao Jia 	}
1867a87ceb3dSWentao Jia 
1868072c089cSLouis Peens 	list_add(&ct_entry->list_node, &zt->pre_ct_list);
1869072c089cSLouis Peens 	zt->pre_ct_count++;
1870072c089cSLouis Peens 
18713c863c30SLouis Peens 	nfp_ct_merge_tc_entries(ct_entry, zt, zt);
18723c863c30SLouis Peens 
18733c863c30SLouis Peens 	/* Need to check and merge with tables in the wc_zone as well */
18743c863c30SLouis Peens 	if (priv->ct_zone_wc)
18753c863c30SLouis Peens 		nfp_ct_merge_tc_entries(ct_entry, priv->ct_zone_wc, zt);
18763c863c30SLouis Peens 
1877d33d24a7SLouis Peens 	return 0;
1878c8b034fbSLouis Peens }
1879c8b034fbSLouis Peens 
nfp_fl_ct_handle_post_ct(struct nfp_flower_priv * priv,struct net_device * netdev,struct flow_cls_offload * flow,struct netlink_ext_ack * extack)1880c8b034fbSLouis Peens int nfp_fl_ct_handle_post_ct(struct nfp_flower_priv *priv,
1881c8b034fbSLouis Peens 			     struct net_device *netdev,
1882c8b034fbSLouis Peens 			     struct flow_cls_offload *flow,
1883c8b034fbSLouis Peens 			     struct netlink_ext_ack *extack)
1884c8b034fbSLouis Peens {
1885bd0fe7f9SLouis Peens 	struct flow_rule *rule = flow_cls_offload_flow_rule(flow);
1886072c089cSLouis Peens 	struct nfp_fl_ct_flow_entry *ct_entry;
188715392de7SHui Zhou 	struct flow_action_entry *ct_goto;
1888bd0fe7f9SLouis Peens 	struct nfp_fl_ct_zone_entry *zt;
188915392de7SHui Zhou 	struct flow_action_entry *act;
1890bd0fe7f9SLouis Peens 	bool wildcarded = false;
1891bd0fe7f9SLouis Peens 	struct flow_match_ct ct;
189215392de7SHui Zhou 	int i;
189315392de7SHui Zhou 
189415392de7SHui Zhou 	flow_action_for_each(i, act, &rule->action) {
189515392de7SHui Zhou 		switch (act->id) {
189615392de7SHui Zhou 		case FLOW_ACTION_REDIRECT:
189715392de7SHui Zhou 		case FLOW_ACTION_REDIRECT_INGRESS:
189815392de7SHui Zhou 		case FLOW_ACTION_MIRRED:
189915392de7SHui Zhou 		case FLOW_ACTION_MIRRED_INGRESS:
190015392de7SHui Zhou 			if (act->dev->rtnl_link_ops &&
190115392de7SHui Zhou 			    !strcmp(act->dev->rtnl_link_ops->kind, "openvswitch")) {
190215392de7SHui Zhou 				NL_SET_ERR_MSG_MOD(extack,
190315392de7SHui Zhou 						   "unsupported offload: out port is openvswitch internal port");
190415392de7SHui Zhou 				return -EOPNOTSUPP;
190515392de7SHui Zhou 			}
190615392de7SHui Zhou 			break;
190715392de7SHui Zhou 		default:
190815392de7SHui Zhou 			break;
190915392de7SHui Zhou 		}
191015392de7SHui Zhou 	}
1911bd0fe7f9SLouis Peens 
1912bd0fe7f9SLouis Peens 	flow_rule_match_ct(rule, &ct);
1913bd0fe7f9SLouis Peens 	if (!ct.mask->ct_zone) {
1914bd0fe7f9SLouis Peens 		wildcarded = true;
1915bd0fe7f9SLouis Peens 	} else if (ct.mask->ct_zone != U16_MAX) {
1916bd0fe7f9SLouis Peens 		NL_SET_ERR_MSG_MOD(extack,
1917bd0fe7f9SLouis Peens 				   "unsupported offload: partially wildcarded ct_zone is not supported");
1918bd0fe7f9SLouis Peens 		return -EOPNOTSUPP;
1919bd0fe7f9SLouis Peens 	}
1920bd0fe7f9SLouis Peens 
1921bd0fe7f9SLouis Peens 	zt = get_nfp_zone_entry(priv, ct.key->ct_zone, wildcarded);
1922bd0fe7f9SLouis Peens 	if (IS_ERR(zt)) {
1923bd0fe7f9SLouis Peens 		NL_SET_ERR_MSG_MOD(extack,
1924bd0fe7f9SLouis Peens 				   "offload error: Could not create zone table entry");
1925bd0fe7f9SLouis Peens 		return PTR_ERR(zt);
1926bd0fe7f9SLouis Peens 	}
1927bd0fe7f9SLouis Peens 
1928072c089cSLouis Peens 	/* Add entry to post_ct_list */
19294772ad3fSYinjun Zhang 	ct_entry = nfp_fl_ct_add_flow(zt, netdev, flow, false, extack);
1930072c089cSLouis Peens 	if (IS_ERR(ct_entry))
1931072c089cSLouis Peens 		return PTR_ERR(ct_entry);
1932072c089cSLouis Peens 
1933072c089cSLouis Peens 	ct_entry->type = CT_TYPE_POST_CT;
1934072c089cSLouis Peens 	ct_entry->chain_index = flow->common.chain_index;
19353e44d199SWentao Jia 	ct_goto = get_flow_act(flow->rule, FLOW_ACTION_GOTO);
19363e44d199SWentao Jia 	ct_entry->goto_chain_index = ct_goto ? ct_goto->chain_index : 0;
1937072c089cSLouis Peens 	list_add(&ct_entry->list_node, &zt->post_ct_list);
1938072c089cSLouis Peens 	zt->post_ct_count++;
1939072c089cSLouis Peens 
19403c863c30SLouis Peens 	if (wildcarded) {
19413c863c30SLouis Peens 		/* Iterate through all zone tables if not empty, look for merges with
19423c863c30SLouis Peens 		 * pre_ct entries and merge them.
19433c863c30SLouis Peens 		 */
19443c863c30SLouis Peens 		struct rhashtable_iter iter;
19453c863c30SLouis Peens 		struct nfp_fl_ct_zone_entry *zone_table;
19463c863c30SLouis Peens 
19473c863c30SLouis Peens 		rhashtable_walk_enter(&priv->ct_zone_table, &iter);
19483c863c30SLouis Peens 		rhashtable_walk_start(&iter);
19493c863c30SLouis Peens 		while ((zone_table = rhashtable_walk_next(&iter)) != NULL) {
19503c863c30SLouis Peens 			if (IS_ERR(zone_table))
19513c863c30SLouis Peens 				continue;
19523c863c30SLouis Peens 			rhashtable_walk_stop(&iter);
19533c863c30SLouis Peens 			nfp_ct_merge_tc_entries(ct_entry, zone_table, zone_table);
19543c863c30SLouis Peens 			rhashtable_walk_start(&iter);
19553c863c30SLouis Peens 		}
19563c863c30SLouis Peens 		rhashtable_walk_stop(&iter);
19573c863c30SLouis Peens 		rhashtable_walk_exit(&iter);
19583c863c30SLouis Peens 	} else {
19593c863c30SLouis Peens 		nfp_ct_merge_tc_entries(ct_entry, zt, zt);
19603c863c30SLouis Peens 	}
19613c863c30SLouis Peens 
1962d33d24a7SLouis Peens 	return 0;
1963d33d24a7SLouis Peens }
1964d33d24a7SLouis Peens 
nfp_fl_create_new_pre_ct(struct nfp_fl_nft_tc_merge * m_entry)1965a87ceb3dSWentao Jia int nfp_fl_create_new_pre_ct(struct nfp_fl_nft_tc_merge *m_entry)
1966a87ceb3dSWentao Jia {
1967a87ceb3dSWentao Jia 	struct nfp_fl_ct_flow_entry *pre_ct_entry, *post_ct_entry;
1968a87ceb3dSWentao Jia 	struct flow_cls_offload new_pre_ct_flow;
1969a87ceb3dSWentao Jia 	int err;
1970a87ceb3dSWentao Jia 
1971a87ceb3dSWentao Jia 	pre_ct_entry = m_entry->tc_m_parent->pre_ct_parent;
1972a87ceb3dSWentao Jia 	if (pre_ct_entry->num_prev_m_entries >= NFP_MAX_RECIRC_CT_ZONES - 1)
1973a87ceb3dSWentao Jia 		return -1;
1974a87ceb3dSWentao Jia 
1975a87ceb3dSWentao Jia 	post_ct_entry = m_entry->tc_m_parent->post_ct_parent;
1976a87ceb3dSWentao Jia 	memset(&new_pre_ct_flow, 0, sizeof(struct flow_cls_offload));
1977a87ceb3dSWentao Jia 	new_pre_ct_flow.rule = post_ct_entry->rule;
1978a87ceb3dSWentao Jia 	new_pre_ct_flow.common.chain_index = post_ct_entry->chain_index;
1979a87ceb3dSWentao Jia 
1980a87ceb3dSWentao Jia 	err = nfp_fl_ct_handle_pre_ct(pre_ct_entry->zt->priv,
1981a87ceb3dSWentao Jia 				      pre_ct_entry->netdev,
1982a87ceb3dSWentao Jia 				      &new_pre_ct_flow, NULL,
1983a87ceb3dSWentao Jia 				      m_entry);
1984a87ceb3dSWentao Jia 	return err;
1985a87ceb3dSWentao Jia }
1986a87ceb3dSWentao Jia 
198740c10bd9SLouis Peens static void
nfp_fl_ct_sub_stats(struct nfp_fl_nft_tc_merge * nft_merge,enum ct_entry_type type,u64 * m_pkts,u64 * m_bytes,u64 * m_used)198840c10bd9SLouis Peens nfp_fl_ct_sub_stats(struct nfp_fl_nft_tc_merge *nft_merge,
198940c10bd9SLouis Peens 		    enum ct_entry_type type, u64 *m_pkts,
199040c10bd9SLouis Peens 		    u64 *m_bytes, u64 *m_used)
199140c10bd9SLouis Peens {
199240c10bd9SLouis Peens 	struct nfp_flower_priv *priv = nft_merge->zt->priv;
199340c10bd9SLouis Peens 	struct nfp_fl_payload *nfp_flow;
199440c10bd9SLouis Peens 	u32 ctx_id;
199540c10bd9SLouis Peens 
199640c10bd9SLouis Peens 	nfp_flow = nft_merge->flow_pay;
199740c10bd9SLouis Peens 	if (!nfp_flow)
199840c10bd9SLouis Peens 		return;
199940c10bd9SLouis Peens 
200040c10bd9SLouis Peens 	ctx_id = be32_to_cpu(nfp_flow->meta.host_ctx_id);
200140c10bd9SLouis Peens 	*m_pkts += priv->stats[ctx_id].pkts;
200240c10bd9SLouis Peens 	*m_bytes += priv->stats[ctx_id].bytes;
200340c10bd9SLouis Peens 	*m_used = max_t(u64, *m_used, priv->stats[ctx_id].used);
200440c10bd9SLouis Peens 
200540c10bd9SLouis Peens 	/* If request is for a sub_flow which is part of a tunnel merged
200640c10bd9SLouis Peens 	 * flow then update stats from tunnel merged flows first.
200740c10bd9SLouis Peens 	 */
200840c10bd9SLouis Peens 	if (!list_empty(&nfp_flow->linked_flows))
200940c10bd9SLouis Peens 		nfp_flower_update_merge_stats(priv->app, nfp_flow);
201040c10bd9SLouis Peens 
201140c10bd9SLouis Peens 	if (type != CT_TYPE_NFT) {
201240c10bd9SLouis Peens 		/* Update nft cached stats */
201340c10bd9SLouis Peens 		flow_stats_update(&nft_merge->nft_parent->stats,
201440c10bd9SLouis Peens 				  priv->stats[ctx_id].bytes,
201540c10bd9SLouis Peens 				  priv->stats[ctx_id].pkts,
201640c10bd9SLouis Peens 				  0, priv->stats[ctx_id].used,
201740c10bd9SLouis Peens 				  FLOW_ACTION_HW_STATS_DELAYED);
201840c10bd9SLouis Peens 	} else {
201940c10bd9SLouis Peens 		/* Update pre_ct cached stats */
202040c10bd9SLouis Peens 		flow_stats_update(&nft_merge->tc_m_parent->pre_ct_parent->stats,
202140c10bd9SLouis Peens 				  priv->stats[ctx_id].bytes,
202240c10bd9SLouis Peens 				  priv->stats[ctx_id].pkts,
202340c10bd9SLouis Peens 				  0, priv->stats[ctx_id].used,
202440c10bd9SLouis Peens 				  FLOW_ACTION_HW_STATS_DELAYED);
202540c10bd9SLouis Peens 		/* Update post_ct cached stats */
202640c10bd9SLouis Peens 		flow_stats_update(&nft_merge->tc_m_parent->post_ct_parent->stats,
202740c10bd9SLouis Peens 				  priv->stats[ctx_id].bytes,
202840c10bd9SLouis Peens 				  priv->stats[ctx_id].pkts,
202940c10bd9SLouis Peens 				  0, priv->stats[ctx_id].used,
203040c10bd9SLouis Peens 				  FLOW_ACTION_HW_STATS_DELAYED);
203140c10bd9SLouis Peens 	}
2032a87ceb3dSWentao Jia 
2033a87ceb3dSWentao Jia 	/* Update previous pre_ct/post_ct/nft flow stats */
2034a87ceb3dSWentao Jia 	if (nft_merge->tc_m_parent->pre_ct_parent->num_prev_m_entries > 0) {
2035a87ceb3dSWentao Jia 		struct nfp_fl_nft_tc_merge *tmp_nft_merge;
2036a87ceb3dSWentao Jia 		int i;
2037a87ceb3dSWentao Jia 
2038a87ceb3dSWentao Jia 		for (i = 0; i < nft_merge->tc_m_parent->pre_ct_parent->num_prev_m_entries; i++) {
2039a87ceb3dSWentao Jia 			tmp_nft_merge = nft_merge->tc_m_parent->pre_ct_parent->prev_m_entries[i];
2040a87ceb3dSWentao Jia 			flow_stats_update(&tmp_nft_merge->tc_m_parent->pre_ct_parent->stats,
2041a87ceb3dSWentao Jia 					  priv->stats[ctx_id].bytes,
2042a87ceb3dSWentao Jia 					  priv->stats[ctx_id].pkts,
2043a87ceb3dSWentao Jia 					  0, priv->stats[ctx_id].used,
2044a87ceb3dSWentao Jia 					  FLOW_ACTION_HW_STATS_DELAYED);
2045a87ceb3dSWentao Jia 			flow_stats_update(&tmp_nft_merge->tc_m_parent->post_ct_parent->stats,
2046a87ceb3dSWentao Jia 					  priv->stats[ctx_id].bytes,
2047a87ceb3dSWentao Jia 					  priv->stats[ctx_id].pkts,
2048a87ceb3dSWentao Jia 					  0, priv->stats[ctx_id].used,
2049a87ceb3dSWentao Jia 					  FLOW_ACTION_HW_STATS_DELAYED);
2050a87ceb3dSWentao Jia 			flow_stats_update(&tmp_nft_merge->nft_parent->stats,
2051a87ceb3dSWentao Jia 					  priv->stats[ctx_id].bytes,
2052a87ceb3dSWentao Jia 					  priv->stats[ctx_id].pkts,
2053a87ceb3dSWentao Jia 					  0, priv->stats[ctx_id].used,
2054a87ceb3dSWentao Jia 					  FLOW_ACTION_HW_STATS_DELAYED);
2055a87ceb3dSWentao Jia 		}
2056a87ceb3dSWentao Jia 	}
2057a87ceb3dSWentao Jia 
205840c10bd9SLouis Peens 	/* Reset stats from the nfp */
205940c10bd9SLouis Peens 	priv->stats[ctx_id].pkts = 0;
206040c10bd9SLouis Peens 	priv->stats[ctx_id].bytes = 0;
206140c10bd9SLouis Peens }
206240c10bd9SLouis Peens 
nfp_fl_ct_stats(struct flow_cls_offload * flow,struct nfp_fl_ct_map_entry * ct_map_ent)206340c10bd9SLouis Peens int nfp_fl_ct_stats(struct flow_cls_offload *flow,
206440c10bd9SLouis Peens 		    struct nfp_fl_ct_map_entry *ct_map_ent)
206540c10bd9SLouis Peens {
206640c10bd9SLouis Peens 	struct nfp_fl_ct_flow_entry *ct_entry = ct_map_ent->ct_entry;
206740c10bd9SLouis Peens 	struct nfp_fl_nft_tc_merge *nft_merge, *nft_m_tmp;
206840c10bd9SLouis Peens 	struct nfp_fl_ct_tc_merge *tc_merge, *tc_m_tmp;
206940c10bd9SLouis Peens 
207040c10bd9SLouis Peens 	u64 pkts = 0, bytes = 0, used = 0;
207140c10bd9SLouis Peens 	u64 m_pkts, m_bytes, m_used;
207240c10bd9SLouis Peens 
207340c10bd9SLouis Peens 	spin_lock_bh(&ct_entry->zt->priv->stats_lock);
207440c10bd9SLouis Peens 
207540c10bd9SLouis Peens 	if (ct_entry->type == CT_TYPE_PRE_CT) {
207640c10bd9SLouis Peens 		/* Iterate tc_merge entries associated with this flow */
207740c10bd9SLouis Peens 		list_for_each_entry_safe(tc_merge, tc_m_tmp, &ct_entry->children,
207840c10bd9SLouis Peens 					 pre_ct_list) {
207940c10bd9SLouis Peens 			m_pkts = 0;
208040c10bd9SLouis Peens 			m_bytes = 0;
208140c10bd9SLouis Peens 			m_used = 0;
208240c10bd9SLouis Peens 			/* Iterate nft_merge entries associated with this tc_merge flow */
208340c10bd9SLouis Peens 			list_for_each_entry_safe(nft_merge, nft_m_tmp, &tc_merge->children,
208440c10bd9SLouis Peens 						 tc_merge_list) {
208540c10bd9SLouis Peens 				nfp_fl_ct_sub_stats(nft_merge, CT_TYPE_PRE_CT,
208640c10bd9SLouis Peens 						    &m_pkts, &m_bytes, &m_used);
208740c10bd9SLouis Peens 			}
208840c10bd9SLouis Peens 			pkts += m_pkts;
208940c10bd9SLouis Peens 			bytes += m_bytes;
209040c10bd9SLouis Peens 			used = max_t(u64, used, m_used);
209140c10bd9SLouis Peens 			/* Update post_ct partner */
209240c10bd9SLouis Peens 			flow_stats_update(&tc_merge->post_ct_parent->stats,
209340c10bd9SLouis Peens 					  m_bytes, m_pkts, 0, m_used,
209440c10bd9SLouis Peens 					  FLOW_ACTION_HW_STATS_DELAYED);
209540c10bd9SLouis Peens 		}
209640c10bd9SLouis Peens 	} else if (ct_entry->type == CT_TYPE_POST_CT) {
209740c10bd9SLouis Peens 		/* Iterate tc_merge entries associated with this flow */
209840c10bd9SLouis Peens 		list_for_each_entry_safe(tc_merge, tc_m_tmp, &ct_entry->children,
209940c10bd9SLouis Peens 					 post_ct_list) {
210040c10bd9SLouis Peens 			m_pkts = 0;
210140c10bd9SLouis Peens 			m_bytes = 0;
210240c10bd9SLouis Peens 			m_used = 0;
210340c10bd9SLouis Peens 			/* Iterate nft_merge entries associated with this tc_merge flow */
210440c10bd9SLouis Peens 			list_for_each_entry_safe(nft_merge, nft_m_tmp, &tc_merge->children,
210540c10bd9SLouis Peens 						 tc_merge_list) {
210640c10bd9SLouis Peens 				nfp_fl_ct_sub_stats(nft_merge, CT_TYPE_POST_CT,
210740c10bd9SLouis Peens 						    &m_pkts, &m_bytes, &m_used);
210840c10bd9SLouis Peens 			}
210940c10bd9SLouis Peens 			pkts += m_pkts;
211040c10bd9SLouis Peens 			bytes += m_bytes;
211140c10bd9SLouis Peens 			used = max_t(u64, used, m_used);
211240c10bd9SLouis Peens 			/* Update pre_ct partner */
211340c10bd9SLouis Peens 			flow_stats_update(&tc_merge->pre_ct_parent->stats,
211440c10bd9SLouis Peens 					  m_bytes, m_pkts, 0, m_used,
211540c10bd9SLouis Peens 					  FLOW_ACTION_HW_STATS_DELAYED);
211640c10bd9SLouis Peens 		}
211740c10bd9SLouis Peens 	} else  {
211840c10bd9SLouis Peens 		/* Iterate nft_merge entries associated with this nft flow */
211940c10bd9SLouis Peens 		list_for_each_entry_safe(nft_merge, nft_m_tmp, &ct_entry->children,
212040c10bd9SLouis Peens 					 nft_flow_list) {
212140c10bd9SLouis Peens 			nfp_fl_ct_sub_stats(nft_merge, CT_TYPE_NFT,
212240c10bd9SLouis Peens 					    &pkts, &bytes, &used);
212340c10bd9SLouis Peens 		}
212440c10bd9SLouis Peens 	}
212540c10bd9SLouis Peens 
212640c10bd9SLouis Peens 	/* Add stats from this request to stats potentially cached by
212740c10bd9SLouis Peens 	 * previous requests.
212840c10bd9SLouis Peens 	 */
212940c10bd9SLouis Peens 	flow_stats_update(&ct_entry->stats, bytes, pkts, 0, used,
213040c10bd9SLouis Peens 			  FLOW_ACTION_HW_STATS_DELAYED);
213140c10bd9SLouis Peens 	/* Finally update the flow stats from the original stats request */
213240c10bd9SLouis Peens 	flow_stats_update(&flow->stats, ct_entry->stats.bytes,
213340c10bd9SLouis Peens 			  ct_entry->stats.pkts, 0,
213440c10bd9SLouis Peens 			  ct_entry->stats.lastused,
213540c10bd9SLouis Peens 			  FLOW_ACTION_HW_STATS_DELAYED);
213640c10bd9SLouis Peens 	/* Stats has been synced to original flow, can now clear
213740c10bd9SLouis Peens 	 * the cache.
213840c10bd9SLouis Peens 	 */
213940c10bd9SLouis Peens 	ct_entry->stats.pkts = 0;
214040c10bd9SLouis Peens 	ct_entry->stats.bytes = 0;
214140c10bd9SLouis Peens 	spin_unlock_bh(&ct_entry->zt->priv->stats_lock);
214240c10bd9SLouis Peens 
214340c10bd9SLouis Peens 	return 0;
214440c10bd9SLouis Peens }
214540c10bd9SLouis Peens 
214629744a10SVlad Buslov static bool
nfp_fl_ct_offload_nft_supported(struct flow_cls_offload * flow)214729744a10SVlad Buslov nfp_fl_ct_offload_nft_supported(struct flow_cls_offload *flow)
214829744a10SVlad Buslov {
214929744a10SVlad Buslov 	struct flow_rule *flow_rule = flow->rule;
215029744a10SVlad Buslov 	struct flow_action *flow_action =
215129744a10SVlad Buslov 		&flow_rule->action;
215229744a10SVlad Buslov 	struct flow_action_entry *act;
215329744a10SVlad Buslov 	int i;
215429744a10SVlad Buslov 
215529744a10SVlad Buslov 	flow_action_for_each(i, act, flow_action) {
215629744a10SVlad Buslov 		if (act->id == FLOW_ACTION_CT_METADATA) {
215729744a10SVlad Buslov 			enum ip_conntrack_info ctinfo =
215829744a10SVlad Buslov 				act->ct_metadata.cookie & NFCT_INFOMASK;
215929744a10SVlad Buslov 
216029744a10SVlad Buslov 			return ctinfo != IP_CT_NEW;
216129744a10SVlad Buslov 		}
216229744a10SVlad Buslov 	}
216329744a10SVlad Buslov 
216429744a10SVlad Buslov 	return false;
216529744a10SVlad Buslov }
216629744a10SVlad Buslov 
216762268e78SLouis Peens static int
nfp_fl_ct_offload_nft_flow(struct nfp_fl_ct_zone_entry * zt,struct flow_cls_offload * flow)216862268e78SLouis Peens nfp_fl_ct_offload_nft_flow(struct nfp_fl_ct_zone_entry *zt, struct flow_cls_offload *flow)
216962268e78SLouis Peens {
217095255017SLouis Peens 	struct nfp_fl_ct_map_entry *ct_map_ent;
217195255017SLouis Peens 	struct nfp_fl_ct_flow_entry *ct_entry;
217295255017SLouis Peens 	struct netlink_ext_ack *extack = NULL;
217395255017SLouis Peens 
217495255017SLouis Peens 	extack = flow->common.extack;
217562268e78SLouis Peens 	switch (flow->command) {
217662268e78SLouis Peens 	case FLOW_CLS_REPLACE:
217729744a10SVlad Buslov 		if (!nfp_fl_ct_offload_nft_supported(flow))
217829744a10SVlad Buslov 			return -EOPNOTSUPP;
217929744a10SVlad Buslov 
218095255017SLouis Peens 		/* Netfilter can request offload multiple times for the same
218195255017SLouis Peens 		 * flow - protect against adding duplicates.
218295255017SLouis Peens 		 */
218395255017SLouis Peens 		ct_map_ent = rhashtable_lookup_fast(&zt->priv->ct_map_table, &flow->cookie,
218495255017SLouis Peens 						    nfp_ct_map_params);
218595255017SLouis Peens 		if (!ct_map_ent) {
21864772ad3fSYinjun Zhang 			ct_entry = nfp_fl_ct_add_flow(zt, NULL, flow, true, extack);
218743c9a811SDan Carpenter 			if (IS_ERR(ct_entry))
218843c9a811SDan Carpenter 				return PTR_ERR(ct_entry);
218995255017SLouis Peens 			ct_entry->type = CT_TYPE_NFT;
219095255017SLouis Peens 			list_add(&ct_entry->list_node, &zt->nft_flows_list);
219195255017SLouis Peens 			zt->nft_flows_count++;
2192a6ffdd3aSLouis Peens 			nfp_ct_merge_nft_with_tc(ct_entry, zt);
219395255017SLouis Peens 		}
219462268e78SLouis Peens 		return 0;
219562268e78SLouis Peens 	case FLOW_CLS_DESTROY:
219695255017SLouis Peens 		ct_map_ent = rhashtable_lookup_fast(&zt->priv->ct_map_table, &flow->cookie,
219795255017SLouis Peens 						    nfp_ct_map_params);
219895255017SLouis Peens 		return nfp_fl_ct_del_flow(ct_map_ent);
219962268e78SLouis Peens 	case FLOW_CLS_STATS:
220040c10bd9SLouis Peens 		ct_map_ent = rhashtable_lookup_fast(&zt->priv->ct_map_table, &flow->cookie,
220140c10bd9SLouis Peens 						    nfp_ct_map_params);
220240c10bd9SLouis Peens 		if (ct_map_ent)
220340c10bd9SLouis Peens 			return nfp_fl_ct_stats(flow, ct_map_ent);
220440c10bd9SLouis Peens 		break;
220562268e78SLouis Peens 	default:
220662268e78SLouis Peens 		break;
220762268e78SLouis Peens 	}
220862268e78SLouis Peens 	return -EINVAL;
220962268e78SLouis Peens }
221062268e78SLouis Peens 
nfp_fl_ct_handle_nft_flow(enum tc_setup_type type,void * type_data,void * cb_priv)221162268e78SLouis Peens int nfp_fl_ct_handle_nft_flow(enum tc_setup_type type, void *type_data, void *cb_priv)
221262268e78SLouis Peens {
221362268e78SLouis Peens 	struct flow_cls_offload *flow = type_data;
221462268e78SLouis Peens 	struct nfp_fl_ct_zone_entry *zt = cb_priv;
221562268e78SLouis Peens 	int err = -EOPNOTSUPP;
221662268e78SLouis Peens 
221762268e78SLouis Peens 	switch (type) {
221862268e78SLouis Peens 	case TC_SETUP_CLSFLOWER:
221914690995SYanguo Li 		while (!mutex_trylock(&zt->priv->nfp_fl_lock)) {
222014690995SYanguo Li 			if (!zt->nft) /* avoid deadlock */
222114690995SYanguo Li 				return err;
222214690995SYanguo Li 			msleep(20);
222314690995SYanguo Li 		}
222462268e78SLouis Peens 		err = nfp_fl_ct_offload_nft_flow(zt, flow);
222514690995SYanguo Li 		mutex_unlock(&zt->priv->nfp_fl_lock);
222662268e78SLouis Peens 		break;
222762268e78SLouis Peens 	default:
222862268e78SLouis Peens 		return -EOPNOTSUPP;
222962268e78SLouis Peens 	}
223062268e78SLouis Peens 	return err;
223162268e78SLouis Peens }
223262268e78SLouis Peens 
223395255017SLouis Peens static void
nfp_fl_ct_clean_nft_entries(struct nfp_fl_ct_zone_entry * zt)223495255017SLouis Peens nfp_fl_ct_clean_nft_entries(struct nfp_fl_ct_zone_entry *zt)
223595255017SLouis Peens {
223695255017SLouis Peens 	struct nfp_fl_ct_flow_entry *nft_entry, *ct_tmp;
223795255017SLouis Peens 	struct nfp_fl_ct_map_entry *ct_map_ent;
223895255017SLouis Peens 
223995255017SLouis Peens 	list_for_each_entry_safe(nft_entry, ct_tmp, &zt->nft_flows_list,
224095255017SLouis Peens 				 list_node) {
224195255017SLouis Peens 		ct_map_ent = rhashtable_lookup_fast(&zt->priv->ct_map_table,
224295255017SLouis Peens 						    &nft_entry->cookie,
224395255017SLouis Peens 						    nfp_ct_map_params);
224495255017SLouis Peens 		nfp_fl_ct_del_flow(ct_map_ent);
224595255017SLouis Peens 	}
224695255017SLouis Peens }
224795255017SLouis Peens 
nfp_fl_ct_del_flow(struct nfp_fl_ct_map_entry * ct_map_ent)2248d33d24a7SLouis Peens int nfp_fl_ct_del_flow(struct nfp_fl_ct_map_entry *ct_map_ent)
2249d33d24a7SLouis Peens {
2250d33d24a7SLouis Peens 	struct nfp_fl_ct_flow_entry *ct_entry;
2251d33d24a7SLouis Peens 	struct nfp_fl_ct_zone_entry *zt;
2252d33d24a7SLouis Peens 	struct rhashtable *m_table;
225314690995SYanguo Li 	struct nf_flowtable *nft;
2254d33d24a7SLouis Peens 
225595255017SLouis Peens 	if (!ct_map_ent)
225695255017SLouis Peens 		return -ENOENT;
225795255017SLouis Peens 
2258d33d24a7SLouis Peens 	zt = ct_map_ent->ct_entry->zt;
2259d33d24a7SLouis Peens 	ct_entry = ct_map_ent->ct_entry;
2260d33d24a7SLouis Peens 	m_table = &zt->priv->ct_map_table;
2261d33d24a7SLouis Peens 
2262d33d24a7SLouis Peens 	switch (ct_entry->type) {
2263d33d24a7SLouis Peens 	case CT_TYPE_PRE_CT:
2264d33d24a7SLouis Peens 		zt->pre_ct_count--;
2265a87ceb3dSWentao Jia 		if (ct_map_ent->cookie > 0)
2266d33d24a7SLouis Peens 			rhashtable_remove_fast(m_table, &ct_map_ent->hash_node,
2267d33d24a7SLouis Peens 					       nfp_ct_map_params);
2268d33d24a7SLouis Peens 		nfp_fl_ct_clean_flow_entry(ct_entry);
2269a87ceb3dSWentao Jia 		if (ct_map_ent->cookie > 0)
2270d33d24a7SLouis Peens 			kfree(ct_map_ent);
227162268e78SLouis Peens 
227214690995SYanguo Li 		if (!zt->pre_ct_count && zt->nft) {
227314690995SYanguo Li 			nft = zt->nft;
227414690995SYanguo Li 			zt->nft = NULL; /* avoid deadlock */
227514690995SYanguo Li 			nf_flow_table_offload_del_cb(nft,
227614690995SYanguo Li 						     nfp_fl_ct_handle_nft_flow,
227714690995SYanguo Li 						     zt);
227895255017SLouis Peens 			nfp_fl_ct_clean_nft_entries(zt);
227962268e78SLouis Peens 		}
2280d33d24a7SLouis Peens 		break;
2281d33d24a7SLouis Peens 	case CT_TYPE_POST_CT:
2282d33d24a7SLouis Peens 		zt->post_ct_count--;
2283d33d24a7SLouis Peens 		rhashtable_remove_fast(m_table, &ct_map_ent->hash_node,
2284d33d24a7SLouis Peens 				       nfp_ct_map_params);
2285d33d24a7SLouis Peens 		nfp_fl_ct_clean_flow_entry(ct_entry);
2286d33d24a7SLouis Peens 		kfree(ct_map_ent);
2287d33d24a7SLouis Peens 		break;
228895255017SLouis Peens 	case CT_TYPE_NFT:
228995255017SLouis Peens 		zt->nft_flows_count--;
229095255017SLouis Peens 		rhashtable_remove_fast(m_table, &ct_map_ent->hash_node,
229195255017SLouis Peens 				       nfp_ct_map_params);
229295255017SLouis Peens 		nfp_fl_ct_clean_flow_entry(ct_map_ent->ct_entry);
229395255017SLouis Peens 		kfree(ct_map_ent);
22944020f26bSGustavo A. R. Silva 		break;
2295d33d24a7SLouis Peens 	default:
2296d33d24a7SLouis Peens 		break;
2297d33d24a7SLouis Peens 	}
2298d33d24a7SLouis Peens 
2299d33d24a7SLouis Peens 	return 0;
2300c8b034fbSLouis Peens }
2301