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