xref: /openbmc/linux/drivers/net/ethernet/marvell/prestera/prestera_flower.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
18b474a9fSSerhiy Boiko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
28b474a9fSSerhiy Boiko /* Copyright (c) 2020 Marvell International Ltd. All rights reserved */
38b474a9fSSerhiy Boiko 
48b474a9fSSerhiy Boiko #include "prestera.h"
58b474a9fSSerhiy Boiko #include "prestera_acl.h"
647327e19SVolodymyr Mytnyk #include "prestera_flow.h"
78b474a9fSSerhiy Boiko #include "prestera_flower.h"
844af9571SMaksym Glubokiy #include "prestera_matchall.h"
98b474a9fSSerhiy Boiko 
10604ba230SVolodymyr Mytnyk struct prestera_flower_template {
11604ba230SVolodymyr Mytnyk 	struct prestera_acl_ruleset *ruleset;
12fa5d824cSVolodymyr Mytnyk 	struct list_head list;
13fa5d824cSVolodymyr Mytnyk 	u32 chain_index;
14604ba230SVolodymyr Mytnyk };
15604ba230SVolodymyr Mytnyk 
16b3ae2d35SVolodymyr Mytnyk static void
prestera_flower_template_free(struct prestera_flower_template * template)17b3ae2d35SVolodymyr Mytnyk prestera_flower_template_free(struct prestera_flower_template *template)
18604ba230SVolodymyr Mytnyk {
19fa5d824cSVolodymyr Mytnyk 	prestera_acl_ruleset_put(template->ruleset);
20fa5d824cSVolodymyr Mytnyk 	list_del(&template->list);
21fa5d824cSVolodymyr Mytnyk 	kfree(template);
22604ba230SVolodymyr Mytnyk }
23b3ae2d35SVolodymyr Mytnyk 
prestera_flower_template_cleanup(struct prestera_flow_block * block)24b3ae2d35SVolodymyr Mytnyk void prestera_flower_template_cleanup(struct prestera_flow_block *block)
25b3ae2d35SVolodymyr Mytnyk {
26b3ae2d35SVolodymyr Mytnyk 	struct prestera_flower_template *template, *tmp;
27b3ae2d35SVolodymyr Mytnyk 
28b3ae2d35SVolodymyr Mytnyk 	/* put the reference to all rulesets kept in tmpl create */
29b3ae2d35SVolodymyr Mytnyk 	list_for_each_entry_safe(template, tmp, &block->template_list, list)
30b3ae2d35SVolodymyr Mytnyk 		prestera_flower_template_free(template);
31604ba230SVolodymyr Mytnyk }
32604ba230SVolodymyr Mytnyk 
33fa5d824cSVolodymyr Mytnyk static int
prestera_flower_parse_goto_action(struct prestera_flow_block * block,struct prestera_acl_rule * rule,u32 chain_index,const struct flow_action_entry * act)34fa5d824cSVolodymyr Mytnyk prestera_flower_parse_goto_action(struct prestera_flow_block *block,
35fa5d824cSVolodymyr Mytnyk 				  struct prestera_acl_rule *rule,
36fa5d824cSVolodymyr Mytnyk 				  u32 chain_index,
37fa5d824cSVolodymyr Mytnyk 				  const struct flow_action_entry *act)
38fa5d824cSVolodymyr Mytnyk {
39fa5d824cSVolodymyr Mytnyk 	struct prestera_acl_ruleset *ruleset;
40fa5d824cSVolodymyr Mytnyk 
41fa5d824cSVolodymyr Mytnyk 	if (act->chain_index <= chain_index)
42fa5d824cSVolodymyr Mytnyk 		/* we can jump only forward */
43fa5d824cSVolodymyr Mytnyk 		return -EINVAL;
44fa5d824cSVolodymyr Mytnyk 
45fa5d824cSVolodymyr Mytnyk 	if (rule->re_arg.jump.valid)
46fa5d824cSVolodymyr Mytnyk 		return -EEXIST;
47fa5d824cSVolodymyr Mytnyk 
48fa5d824cSVolodymyr Mytnyk 	ruleset = prestera_acl_ruleset_get(block->sw->acl, block,
49fa5d824cSVolodymyr Mytnyk 					   act->chain_index);
50fa5d824cSVolodymyr Mytnyk 	if (IS_ERR(ruleset))
51fa5d824cSVolodymyr Mytnyk 		return PTR_ERR(ruleset);
52fa5d824cSVolodymyr Mytnyk 
53fa5d824cSVolodymyr Mytnyk 	rule->re_arg.jump.valid = 1;
54fa5d824cSVolodymyr Mytnyk 	rule->re_arg.jump.i.index = prestera_acl_ruleset_index_get(ruleset);
55fa5d824cSVolodymyr Mytnyk 
56fa5d824cSVolodymyr Mytnyk 	rule->jump_ruleset = ruleset;
57fa5d824cSVolodymyr Mytnyk 
58fa5d824cSVolodymyr Mytnyk 	return 0;
59fa5d824cSVolodymyr Mytnyk }
60fa5d824cSVolodymyr Mytnyk 
prestera_flower_parse_actions(struct prestera_flow_block * block,struct prestera_acl_rule * rule,struct flow_action * flow_action,u32 chain_index,struct netlink_ext_ack * extack)618b474a9fSSerhiy Boiko static int prestera_flower_parse_actions(struct prestera_flow_block *block,
628b474a9fSSerhiy Boiko 					 struct prestera_acl_rule *rule,
638b474a9fSSerhiy Boiko 					 struct flow_action *flow_action,
64fa5d824cSVolodymyr Mytnyk 					 u32 chain_index,
658b474a9fSSerhiy Boiko 					 struct netlink_ext_ack *extack)
668b474a9fSSerhiy Boiko {
678b474a9fSSerhiy Boiko 	const struct flow_action_entry *act;
68fa5d824cSVolodymyr Mytnyk 	int err, i;
698b474a9fSSerhiy Boiko 
7047327e19SVolodymyr Mytnyk 	/* whole struct (rule->re_arg) must be initialized with 0 */
718b474a9fSSerhiy Boiko 	if (!flow_action_has_entries(flow_action))
728b474a9fSSerhiy Boiko 		return 0;
738b474a9fSSerhiy Boiko 
74e8bd7025SVolodymyr Mytnyk 	if (!flow_action_mixed_hw_stats_check(flow_action, extack))
75e8bd7025SVolodymyr Mytnyk 		return -EOPNOTSUPP;
76e8bd7025SVolodymyr Mytnyk 
77e8bd7025SVolodymyr Mytnyk 	act = flow_action_first_entry_get(flow_action);
78e8bd7025SVolodymyr Mytnyk 	if (act->hw_stats & FLOW_ACTION_HW_STATS_DISABLED) {
79e8bd7025SVolodymyr Mytnyk 		/* Nothing to do */
80e8bd7025SVolodymyr Mytnyk 	} else if (act->hw_stats & FLOW_ACTION_HW_STATS_DELAYED) {
81e8bd7025SVolodymyr Mytnyk 		/* setup counter first */
82e8bd7025SVolodymyr Mytnyk 		rule->re_arg.count.valid = true;
83702e7014SMaksym Glubokiy 		err = prestera_acl_chain_to_client(chain_index, block->ingress,
84e8bd7025SVolodymyr Mytnyk 						   &rule->re_arg.count.client);
85e8bd7025SVolodymyr Mytnyk 		if (err)
86e8bd7025SVolodymyr Mytnyk 			return err;
87e8bd7025SVolodymyr Mytnyk 	} else {
88e8bd7025SVolodymyr Mytnyk 		NL_SET_ERR_MSG_MOD(extack, "Unsupported action HW stats type");
89e8bd7025SVolodymyr Mytnyk 		return -EOPNOTSUPP;
90e8bd7025SVolodymyr Mytnyk 	}
91e8bd7025SVolodymyr Mytnyk 
928b474a9fSSerhiy Boiko 	flow_action_for_each(i, act, flow_action) {
938b474a9fSSerhiy Boiko 		switch (act->id) {
948b474a9fSSerhiy Boiko 		case FLOW_ACTION_ACCEPT:
9547327e19SVolodymyr Mytnyk 			if (rule->re_arg.accept.valid)
9647327e19SVolodymyr Mytnyk 				return -EEXIST;
9747327e19SVolodymyr Mytnyk 
9847327e19SVolodymyr Mytnyk 			rule->re_arg.accept.valid = 1;
998b474a9fSSerhiy Boiko 			break;
1008b474a9fSSerhiy Boiko 		case FLOW_ACTION_DROP:
10147327e19SVolodymyr Mytnyk 			if (rule->re_arg.drop.valid)
10247327e19SVolodymyr Mytnyk 				return -EEXIST;
10347327e19SVolodymyr Mytnyk 
10447327e19SVolodymyr Mytnyk 			rule->re_arg.drop.valid = 1;
1058b474a9fSSerhiy Boiko 			break;
1068b474a9fSSerhiy Boiko 		case FLOW_ACTION_TRAP:
10747327e19SVolodymyr Mytnyk 			if (rule->re_arg.trap.valid)
10847327e19SVolodymyr Mytnyk 				return -EEXIST;
10947327e19SVolodymyr Mytnyk 
11047327e19SVolodymyr Mytnyk 			rule->re_arg.trap.valid = 1;
1118b474a9fSSerhiy Boiko 			break;
112dde2daa0SVolodymyr Mytnyk 		case FLOW_ACTION_POLICE:
113dde2daa0SVolodymyr Mytnyk 			if (rule->re_arg.police.valid)
114dde2daa0SVolodymyr Mytnyk 				return -EEXIST;
115dde2daa0SVolodymyr Mytnyk 
116dde2daa0SVolodymyr Mytnyk 			rule->re_arg.police.valid = 1;
117dde2daa0SVolodymyr Mytnyk 			rule->re_arg.police.rate =
118dde2daa0SVolodymyr Mytnyk 				act->police.rate_bytes_ps;
119dde2daa0SVolodymyr Mytnyk 			rule->re_arg.police.burst = act->police.burst;
1203c6aca33SMaksym Glubokiy 			rule->re_arg.police.ingress = block->ingress;
121dde2daa0SVolodymyr Mytnyk 			break;
122fa5d824cSVolodymyr Mytnyk 		case FLOW_ACTION_GOTO:
123fa5d824cSVolodymyr Mytnyk 			err = prestera_flower_parse_goto_action(block, rule,
124fa5d824cSVolodymyr Mytnyk 								chain_index,
125fa5d824cSVolodymyr Mytnyk 								act);
126fa5d824cSVolodymyr Mytnyk 			if (err)
127fa5d824cSVolodymyr Mytnyk 				return err;
128fa5d824cSVolodymyr Mytnyk 			break;
1298b474a9fSSerhiy Boiko 		default:
1308b474a9fSSerhiy Boiko 			NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
1318b474a9fSSerhiy Boiko 			pr_err("Unsupported action\n");
1328b474a9fSSerhiy Boiko 			return -EOPNOTSUPP;
1338b474a9fSSerhiy Boiko 		}
1348b474a9fSSerhiy Boiko 	}
1358b474a9fSSerhiy Boiko 
1368b474a9fSSerhiy Boiko 	return 0;
1378b474a9fSSerhiy Boiko }
1388b474a9fSSerhiy Boiko 
prestera_flower_parse_meta(struct prestera_acl_rule * rule,struct flow_cls_offload * f,struct prestera_flow_block * block)1398b474a9fSSerhiy Boiko static int prestera_flower_parse_meta(struct prestera_acl_rule *rule,
1408b474a9fSSerhiy Boiko 				      struct flow_cls_offload *f,
1418b474a9fSSerhiy Boiko 				      struct prestera_flow_block *block)
14271c47aa9SMaksym Glubokiy {
14371c47aa9SMaksym Glubokiy 	struct flow_rule *f_rule = flow_cls_offload_flow_rule(f);
14447327e19SVolodymyr Mytnyk 	struct prestera_acl_match *r_match = &rule->re_key.match;
14547327e19SVolodymyr Mytnyk 	struct prestera_port *port;
1468b474a9fSSerhiy Boiko 	struct net_device *ingress_dev;
1478b474a9fSSerhiy Boiko 	struct flow_match_meta match;
14847327e19SVolodymyr Mytnyk 	__be16 key, mask;
1498b474a9fSSerhiy Boiko 
1508b474a9fSSerhiy Boiko 	flow_rule_match_meta(f_rule, &match);
151f4356947SIdo Schimmel 
152f4356947SIdo Schimmel 	if (match.mask->l2_miss) {
153f4356947SIdo Schimmel 		NL_SET_ERR_MSG_MOD(f->common.extack, "Can't match on \"l2_miss\"");
154f4356947SIdo Schimmel 		return -EOPNOTSUPP;
155f4356947SIdo Schimmel 	}
156f4356947SIdo Schimmel 
1578b474a9fSSerhiy Boiko 	if (match.mask->ingress_ifindex != 0xFFFFFFFF) {
1588b474a9fSSerhiy Boiko 		NL_SET_ERR_MSG_MOD(f->common.extack,
1598b474a9fSSerhiy Boiko 				   "Unsupported ingress ifindex mask");
1608b474a9fSSerhiy Boiko 		return -EINVAL;
1618b474a9fSSerhiy Boiko 	}
1628b474a9fSSerhiy Boiko 
16347327e19SVolodymyr Mytnyk 	ingress_dev = __dev_get_by_index(block->net,
1648b474a9fSSerhiy Boiko 					 match.key->ingress_ifindex);
1658b474a9fSSerhiy Boiko 	if (!ingress_dev) {
1668b474a9fSSerhiy Boiko 		NL_SET_ERR_MSG_MOD(f->common.extack,
1678b474a9fSSerhiy Boiko 				   "Can't find specified ingress port to match on");
1688b474a9fSSerhiy Boiko 		return -EINVAL;
1698b474a9fSSerhiy Boiko 	}
1708b474a9fSSerhiy Boiko 
1718b474a9fSSerhiy Boiko 	if (!prestera_netdev_check(ingress_dev)) {
1728b474a9fSSerhiy Boiko 		NL_SET_ERR_MSG_MOD(f->common.extack,
1738b474a9fSSerhiy Boiko 				   "Can't match on switchdev ingress port");
1748b474a9fSSerhiy Boiko 		return -EINVAL;
1758b474a9fSSerhiy Boiko 	}
1768b474a9fSSerhiy Boiko 	port = netdev_priv(ingress_dev);
1778b474a9fSSerhiy Boiko 
1781e20904eSMaksym Glubokiy 	mask = htons(0x1FFF << 3);
1791e20904eSMaksym Glubokiy 	key = htons(port->hw_id << 3);
18047327e19SVolodymyr Mytnyk 	rule_match_set(r_match->key, SYS_PORT, key);
18147327e19SVolodymyr Mytnyk 	rule_match_set(r_match->mask, SYS_PORT, mask);
1828b474a9fSSerhiy Boiko 
1831e20904eSMaksym Glubokiy 	mask = htons(0x3FF);
18447327e19SVolodymyr Mytnyk 	key = htons(port->dev_id);
18547327e19SVolodymyr Mytnyk 	rule_match_set(r_match->key, SYS_DEV, key);
18647327e19SVolodymyr Mytnyk 	rule_match_set(r_match->mask, SYS_DEV, mask);
18747327e19SVolodymyr Mytnyk 
18847327e19SVolodymyr Mytnyk 	return 0;
1898b474a9fSSerhiy Boiko }
1908b474a9fSSerhiy Boiko 
prestera_flower_parse(struct prestera_flow_block * block,struct prestera_acl_rule * rule,struct flow_cls_offload * f)1918b474a9fSSerhiy Boiko static int prestera_flower_parse(struct prestera_flow_block *block,
1928b474a9fSSerhiy Boiko 				 struct prestera_acl_rule *rule,
1938b474a9fSSerhiy Boiko 				 struct flow_cls_offload *f)
19471c47aa9SMaksym Glubokiy {
19571c47aa9SMaksym Glubokiy 	struct flow_rule *f_rule = flow_cls_offload_flow_rule(f);
1968b474a9fSSerhiy Boiko 	struct flow_dissector *dissector = f_rule->match.dissector;
19747327e19SVolodymyr Mytnyk 	struct prestera_acl_match *r_match = &rule->re_key.match;
19847327e19SVolodymyr Mytnyk 	__be16 n_proto_mask = 0;
19947327e19SVolodymyr Mytnyk 	__be16 n_proto_key = 0;
2008b474a9fSSerhiy Boiko 	u16 addr_type = 0;
2018b474a9fSSerhiy Boiko 	u8 ip_proto = 0;
2028b474a9fSSerhiy Boiko 	int err;
2038b474a9fSSerhiy Boiko 
2048b474a9fSSerhiy Boiko 	if (dissector->used_keys &
205*2b3082c6SRatheesh Kannoth 	    ~(BIT_ULL(FLOW_DISSECTOR_KEY_META) |
206*2b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |
207*2b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
208*2b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
209*2b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
210*2b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
211*2b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_ICMP) |
212*2b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) |
213*2b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_PORTS_RANGE) |
214*2b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_VLAN))) {
2158b474a9fSSerhiy Boiko 		NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported key");
2168b474a9fSSerhiy Boiko 		return -EOPNOTSUPP;
2178b474a9fSSerhiy Boiko 	}
2188b474a9fSSerhiy Boiko 
2198b474a9fSSerhiy Boiko 	prestera_acl_rule_priority_set(rule, f->common.prio);
2208b474a9fSSerhiy Boiko 
2218b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_META)) {
2228b474a9fSSerhiy Boiko 		err = prestera_flower_parse_meta(rule, f, block);
2238b474a9fSSerhiy Boiko 		if (err)
2248b474a9fSSerhiy Boiko 			return err;
2258b474a9fSSerhiy Boiko 	}
2268b474a9fSSerhiy Boiko 
2278b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_CONTROL)) {
2288b474a9fSSerhiy Boiko 		struct flow_match_control match;
2298b474a9fSSerhiy Boiko 
2308b474a9fSSerhiy Boiko 		flow_rule_match_control(f_rule, &match);
2318b474a9fSSerhiy Boiko 		addr_type = match.key->addr_type;
2328b474a9fSSerhiy Boiko 	}
2338b474a9fSSerhiy Boiko 
2348b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_BASIC)) {
2358b474a9fSSerhiy Boiko 		struct flow_match_basic match;
2368b474a9fSSerhiy Boiko 
2378b474a9fSSerhiy Boiko 		flow_rule_match_basic(f_rule, &match);
23847327e19SVolodymyr Mytnyk 		n_proto_key = match.key->n_proto;
23947327e19SVolodymyr Mytnyk 		n_proto_mask = match.mask->n_proto;
2408b474a9fSSerhiy Boiko 
24147327e19SVolodymyr Mytnyk 		if (ntohs(match.key->n_proto) == ETH_P_ALL) {
2428b474a9fSSerhiy Boiko 			n_proto_key = 0;
2438b474a9fSSerhiy Boiko 			n_proto_mask = 0;
2448b474a9fSSerhiy Boiko 		}
2458b474a9fSSerhiy Boiko 
24647327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, ETH_TYPE, n_proto_key);
24747327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, ETH_TYPE, n_proto_mask);
2488b474a9fSSerhiy Boiko 
24947327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, IP_PROTO, match.key->ip_proto);
25047327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, IP_PROTO, match.mask->ip_proto);
2518b474a9fSSerhiy Boiko 		ip_proto = match.key->ip_proto;
2528b474a9fSSerhiy Boiko 	}
2538b474a9fSSerhiy Boiko 
2548b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
2558b474a9fSSerhiy Boiko 		struct flow_match_eth_addrs match;
2568b474a9fSSerhiy Boiko 
2578b474a9fSSerhiy Boiko 		flow_rule_match_eth_addrs(f_rule, &match);
2588b474a9fSSerhiy Boiko 
25947327e19SVolodymyr Mytnyk 		/* DA key, mask */
26047327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->key,
26147327e19SVolodymyr Mytnyk 				 ETH_DMAC_0, &match.key->dst[0], 4);
26247327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->key,
26347327e19SVolodymyr Mytnyk 				 ETH_DMAC_1, &match.key->dst[4], 2);
2648b474a9fSSerhiy Boiko 
26547327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->mask,
26647327e19SVolodymyr Mytnyk 				 ETH_DMAC_0, &match.mask->dst[0], 4);
26747327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->mask,
26847327e19SVolodymyr Mytnyk 				 ETH_DMAC_1, &match.mask->dst[4], 2);
26947327e19SVolodymyr Mytnyk 
27047327e19SVolodymyr Mytnyk 		/* SA key, mask */
27147327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->key,
27247327e19SVolodymyr Mytnyk 				 ETH_SMAC_0, &match.key->src[0], 4);
27347327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->key,
27447327e19SVolodymyr Mytnyk 				 ETH_SMAC_1, &match.key->src[4], 2);
27547327e19SVolodymyr Mytnyk 
27647327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->mask,
27747327e19SVolodymyr Mytnyk 				 ETH_SMAC_0, &match.mask->src[0], 4);
27847327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->mask,
27947327e19SVolodymyr Mytnyk 				 ETH_SMAC_1, &match.mask->src[4], 2);
2808b474a9fSSerhiy Boiko 	}
2818b474a9fSSerhiy Boiko 
2828b474a9fSSerhiy Boiko 	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
2838b474a9fSSerhiy Boiko 		struct flow_match_ipv4_addrs match;
2848b474a9fSSerhiy Boiko 
2858b474a9fSSerhiy Boiko 		flow_rule_match_ipv4_addrs(f_rule, &match);
2868b474a9fSSerhiy Boiko 
28747327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, IP_SRC, match.key->src);
28847327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, IP_SRC, match.mask->src);
2898b474a9fSSerhiy Boiko 
29047327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, IP_DST, match.key->dst);
29147327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, IP_DST, match.mask->dst);
2928b474a9fSSerhiy Boiko 	}
2938b474a9fSSerhiy Boiko 
2948b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_PORTS)) {
2958b474a9fSSerhiy Boiko 		struct flow_match_ports match;
2968b474a9fSSerhiy Boiko 
2978b474a9fSSerhiy Boiko 		if (ip_proto != IPPROTO_TCP && ip_proto != IPPROTO_UDP) {
2988b474a9fSSerhiy Boiko 			NL_SET_ERR_MSG_MOD
2998b474a9fSSerhiy Boiko 			    (f->common.extack,
3008b474a9fSSerhiy Boiko 			     "Only UDP and TCP keys are supported");
3018b474a9fSSerhiy Boiko 			return -EINVAL;
3028b474a9fSSerhiy Boiko 		}
3038b474a9fSSerhiy Boiko 
3048b474a9fSSerhiy Boiko 		flow_rule_match_ports(f_rule, &match);
3058b474a9fSSerhiy Boiko 
30647327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, L4_PORT_SRC, match.key->src);
30747327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, L4_PORT_SRC, match.mask->src);
3088b474a9fSSerhiy Boiko 
30947327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, L4_PORT_DST, match.key->dst);
31047327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, L4_PORT_DST, match.mask->dst);
3118b474a9fSSerhiy Boiko 	}
3128b474a9fSSerhiy Boiko 
313551871bfSMaksym Glubokiy 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_PORTS_RANGE)) {
314551871bfSMaksym Glubokiy 		struct flow_match_ports_range match;
315551871bfSMaksym Glubokiy 		__be32 tp_key, tp_mask;
316551871bfSMaksym Glubokiy 
317551871bfSMaksym Glubokiy 		flow_rule_match_ports_range(f_rule, &match);
318551871bfSMaksym Glubokiy 
319551871bfSMaksym Glubokiy 		/* src port range (min, max) */
320551871bfSMaksym Glubokiy 		tp_key = htonl(ntohs(match.key->tp_min.src) |
321551871bfSMaksym Glubokiy 			       (ntohs(match.key->tp_max.src) << 16));
322551871bfSMaksym Glubokiy 		tp_mask = htonl(ntohs(match.mask->tp_min.src) |
323551871bfSMaksym Glubokiy 				(ntohs(match.mask->tp_max.src) << 16));
324551871bfSMaksym Glubokiy 		rule_match_set(r_match->key, L4_PORT_RANGE_SRC, tp_key);
325551871bfSMaksym Glubokiy 		rule_match_set(r_match->mask, L4_PORT_RANGE_SRC, tp_mask);
326551871bfSMaksym Glubokiy 
327551871bfSMaksym Glubokiy 		/* dst port range (min, max) */
328551871bfSMaksym Glubokiy 		tp_key = htonl(ntohs(match.key->tp_min.dst) |
329551871bfSMaksym Glubokiy 			       (ntohs(match.key->tp_max.dst) << 16));
330551871bfSMaksym Glubokiy 		tp_mask = htonl(ntohs(match.mask->tp_min.dst) |
331551871bfSMaksym Glubokiy 				(ntohs(match.mask->tp_max.dst) << 16));
332551871bfSMaksym Glubokiy 		rule_match_set(r_match->key, L4_PORT_RANGE_DST, tp_key);
333551871bfSMaksym Glubokiy 		rule_match_set(r_match->mask, L4_PORT_RANGE_DST, tp_mask);
334551871bfSMaksym Glubokiy 	}
335551871bfSMaksym Glubokiy 
3368b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_VLAN)) {
3378b474a9fSSerhiy Boiko 		struct flow_match_vlan match;
3388b474a9fSSerhiy Boiko 
3398b474a9fSSerhiy Boiko 		flow_rule_match_vlan(f_rule, &match);
3408b474a9fSSerhiy Boiko 
3418b474a9fSSerhiy Boiko 		if (match.mask->vlan_id != 0) {
34247327e19SVolodymyr Mytnyk 			__be16 key = cpu_to_be16(match.key->vlan_id);
34347327e19SVolodymyr Mytnyk 			__be16 mask = cpu_to_be16(match.mask->vlan_id);
34447327e19SVolodymyr Mytnyk 
34547327e19SVolodymyr Mytnyk 			rule_match_set(r_match->key, VLAN_ID, key);
34647327e19SVolodymyr Mytnyk 			rule_match_set(r_match->mask, VLAN_ID, mask);
3478b474a9fSSerhiy Boiko 		}
3488b474a9fSSerhiy Boiko 
34947327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, VLAN_TPID, match.key->vlan_tpid);
35047327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, VLAN_TPID, match.mask->vlan_tpid);
3518b474a9fSSerhiy Boiko 	}
3528b474a9fSSerhiy Boiko 
3538b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_ICMP)) {
3548b474a9fSSerhiy Boiko 		struct flow_match_icmp match;
3558b474a9fSSerhiy Boiko 
3568b474a9fSSerhiy Boiko 		flow_rule_match_icmp(f_rule, &match);
3578b474a9fSSerhiy Boiko 
35847327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, ICMP_TYPE, match.key->type);
35947327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, ICMP_TYPE, match.mask->type);
3608b474a9fSSerhiy Boiko 
36147327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, ICMP_CODE, match.key->code);
36247327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, ICMP_CODE, match.mask->code);
3638b474a9fSSerhiy Boiko 	}
3648b474a9fSSerhiy Boiko 
36547327e19SVolodymyr Mytnyk 	return prestera_flower_parse_actions(block, rule, &f->rule->action,
366fa5d824cSVolodymyr Mytnyk 					     f->common.chain_index,
3678b474a9fSSerhiy Boiko 					     f->common.extack);
3688b474a9fSSerhiy Boiko }
3698b474a9fSSerhiy Boiko 
prestera_flower_prio_check(struct prestera_flow_block * block,struct flow_cls_offload * f)37044af9571SMaksym Glubokiy static int prestera_flower_prio_check(struct prestera_flow_block *block,
37144af9571SMaksym Glubokiy 				      struct flow_cls_offload *f)
37244af9571SMaksym Glubokiy {
37344af9571SMaksym Glubokiy 	u32 mall_prio_min;
37444af9571SMaksym Glubokiy 	u32 mall_prio_max;
37544af9571SMaksym Glubokiy 	int err;
37644af9571SMaksym Glubokiy 
37744af9571SMaksym Glubokiy 	err = prestera_mall_prio_get(block, &mall_prio_min, &mall_prio_max);
37844af9571SMaksym Glubokiy 	if (err == -ENOENT)
37944af9571SMaksym Glubokiy 		/* No matchall filters installed on this chain. */
38044af9571SMaksym Glubokiy 		return 0;
38144af9571SMaksym Glubokiy 
38244af9571SMaksym Glubokiy 	if (err) {
38344af9571SMaksym Glubokiy 		NL_SET_ERR_MSG(f->common.extack, "Failed to get matchall priorities");
38444af9571SMaksym Glubokiy 		return err;
38544af9571SMaksym Glubokiy 	}
38644af9571SMaksym Glubokiy 
38744af9571SMaksym Glubokiy 	if (f->common.prio <= mall_prio_max && block->ingress) {
38844af9571SMaksym Glubokiy 		NL_SET_ERR_MSG(f->common.extack,
38944af9571SMaksym Glubokiy 			       "Failed to add in front of existing matchall rules");
39044af9571SMaksym Glubokiy 		return -EOPNOTSUPP;
39144af9571SMaksym Glubokiy 	}
39244af9571SMaksym Glubokiy 	if (f->common.prio >= mall_prio_min && !block->ingress) {
39344af9571SMaksym Glubokiy 		NL_SET_ERR_MSG(f->common.extack, "Failed to add behind of existing matchall rules");
39444af9571SMaksym Glubokiy 		return -EOPNOTSUPP;
39544af9571SMaksym Glubokiy 	}
39644af9571SMaksym Glubokiy 
39744af9571SMaksym Glubokiy 	return 0;
39844af9571SMaksym Glubokiy }
39944af9571SMaksym Glubokiy 
prestera_flower_prio_get(struct prestera_flow_block * block,u32 chain_index,u32 * prio_min,u32 * prio_max)40044af9571SMaksym Glubokiy int prestera_flower_prio_get(struct prestera_flow_block *block, u32 chain_index,
40144af9571SMaksym Glubokiy 			     u32 *prio_min, u32 *prio_max)
40244af9571SMaksym Glubokiy {
40344af9571SMaksym Glubokiy 	struct prestera_acl_ruleset *ruleset;
40444af9571SMaksym Glubokiy 
40544af9571SMaksym Glubokiy 	ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block, chain_index);
40644af9571SMaksym Glubokiy 	if (IS_ERR(ruleset))
40744af9571SMaksym Glubokiy 		return PTR_ERR(ruleset);
40844af9571SMaksym Glubokiy 
40944af9571SMaksym Glubokiy 	prestera_acl_ruleset_prio_get(ruleset, prio_min, prio_max);
41044af9571SMaksym Glubokiy 	return 0;
41144af9571SMaksym Glubokiy }
41244af9571SMaksym Glubokiy 
prestera_flower_replace(struct prestera_flow_block * block,struct flow_cls_offload * f)4138b474a9fSSerhiy Boiko int prestera_flower_replace(struct prestera_flow_block *block,
4148b474a9fSSerhiy Boiko 			    struct flow_cls_offload *f)
4158b474a9fSSerhiy Boiko {
41647327e19SVolodymyr Mytnyk 	struct prestera_acl_ruleset *ruleset;
41747327e19SVolodymyr Mytnyk 	struct prestera_acl *acl = block->sw->acl;
4188b474a9fSSerhiy Boiko 	struct prestera_acl_rule *rule;
4198b474a9fSSerhiy Boiko 	int err;
4208b474a9fSSerhiy Boiko 
42144af9571SMaksym Glubokiy 	err = prestera_flower_prio_check(block, f);
42244af9571SMaksym Glubokiy 	if (err)
42344af9571SMaksym Glubokiy 		return err;
42444af9571SMaksym Glubokiy 
425fa5d824cSVolodymyr Mytnyk 	ruleset = prestera_acl_ruleset_get(acl, block, f->common.chain_index);
42647327e19SVolodymyr Mytnyk 	if (IS_ERR(ruleset))
42747327e19SVolodymyr Mytnyk 		return PTR_ERR(ruleset);
42847327e19SVolodymyr Mytnyk 
42947327e19SVolodymyr Mytnyk 	/* increments the ruleset reference */
430fa5d824cSVolodymyr Mytnyk 	rule = prestera_acl_rule_create(ruleset, f->cookie,
431fa5d824cSVolodymyr Mytnyk 					f->common.chain_index);
43247327e19SVolodymyr Mytnyk 	if (IS_ERR(rule)) {
43347327e19SVolodymyr Mytnyk 		err = PTR_ERR(rule);
43447327e19SVolodymyr Mytnyk 		goto err_rule_create;
43547327e19SVolodymyr Mytnyk 	}
4368b474a9fSSerhiy Boiko 
4378b474a9fSSerhiy Boiko 	err = prestera_flower_parse(block, rule, f);
4388b474a9fSSerhiy Boiko 	if (err)
43947327e19SVolodymyr Mytnyk 		goto err_rule_add;
4408b474a9fSSerhiy Boiko 
44147327e19SVolodymyr Mytnyk 	if (!prestera_acl_ruleset_is_offload(ruleset)) {
44247327e19SVolodymyr Mytnyk 		err = prestera_acl_ruleset_offload(ruleset);
44347327e19SVolodymyr Mytnyk 		if (err)
44447327e19SVolodymyr Mytnyk 			goto err_ruleset_offload;
44547327e19SVolodymyr Mytnyk 	}
44647327e19SVolodymyr Mytnyk 
44747327e19SVolodymyr Mytnyk 	err = prestera_acl_rule_add(block->sw, rule);
4488b474a9fSSerhiy Boiko 	if (err)
4498b474a9fSSerhiy Boiko 		goto err_rule_add;
4508b474a9fSSerhiy Boiko 
45147327e19SVolodymyr Mytnyk 	prestera_acl_ruleset_put(ruleset);
4528b474a9fSSerhiy Boiko 	return 0;
4538b474a9fSSerhiy Boiko 
45447327e19SVolodymyr Mytnyk err_ruleset_offload:
4558b474a9fSSerhiy Boiko err_rule_add:
4568b474a9fSSerhiy Boiko 	prestera_acl_rule_destroy(rule);
45747327e19SVolodymyr Mytnyk err_rule_create:
45847327e19SVolodymyr Mytnyk 	prestera_acl_ruleset_put(ruleset);
4598b474a9fSSerhiy Boiko 	return err;
4608b474a9fSSerhiy Boiko }
4618b474a9fSSerhiy Boiko 
prestera_flower_destroy(struct prestera_flow_block * block,struct flow_cls_offload * f)4628b474a9fSSerhiy Boiko void prestera_flower_destroy(struct prestera_flow_block *block,
4638b474a9fSSerhiy Boiko 			     struct flow_cls_offload *f)
4648b474a9fSSerhiy Boiko {
46547327e19SVolodymyr Mytnyk 	struct prestera_acl_ruleset *ruleset;
4668b474a9fSSerhiy Boiko 	struct prestera_acl_rule *rule;
4678b474a9fSSerhiy Boiko 
468fa5d824cSVolodymyr Mytnyk 	ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block,
469fa5d824cSVolodymyr Mytnyk 					      f->common.chain_index);
47047327e19SVolodymyr Mytnyk 	if (IS_ERR(ruleset))
47147327e19SVolodymyr Mytnyk 		return;
47247327e19SVolodymyr Mytnyk 
47347327e19SVolodymyr Mytnyk 	rule = prestera_acl_rule_lookup(ruleset, f->cookie);
4748b474a9fSSerhiy Boiko 	if (rule) {
47547327e19SVolodymyr Mytnyk 		prestera_acl_rule_del(block->sw, rule);
4768b474a9fSSerhiy Boiko 		prestera_acl_rule_destroy(rule);
4778b474a9fSSerhiy Boiko 	}
47847327e19SVolodymyr Mytnyk 	prestera_acl_ruleset_put(ruleset);
4798b474a9fSSerhiy Boiko }
4808b474a9fSSerhiy Boiko 
prestera_flower_tmplt_create(struct prestera_flow_block * block,struct flow_cls_offload * f)481604ba230SVolodymyr Mytnyk int prestera_flower_tmplt_create(struct prestera_flow_block *block,
482604ba230SVolodymyr Mytnyk 				 struct flow_cls_offload *f)
483604ba230SVolodymyr Mytnyk {
484604ba230SVolodymyr Mytnyk 	struct prestera_flower_template *template;
485604ba230SVolodymyr Mytnyk 	struct prestera_acl_ruleset *ruleset;
486604ba230SVolodymyr Mytnyk 	struct prestera_acl_rule rule;
487604ba230SVolodymyr Mytnyk 	int err;
488604ba230SVolodymyr Mytnyk 
489604ba230SVolodymyr Mytnyk 	memset(&rule, 0, sizeof(rule));
490604ba230SVolodymyr Mytnyk 	err = prestera_flower_parse(block, &rule, f);
491604ba230SVolodymyr Mytnyk 	if (err)
492604ba230SVolodymyr Mytnyk 		return err;
493604ba230SVolodymyr Mytnyk 
494604ba230SVolodymyr Mytnyk 	template = kmalloc(sizeof(*template), GFP_KERNEL);
495604ba230SVolodymyr Mytnyk 	if (!template) {
496604ba230SVolodymyr Mytnyk 		err = -ENOMEM;
497604ba230SVolodymyr Mytnyk 		goto err_malloc;
498604ba230SVolodymyr Mytnyk 	}
499604ba230SVolodymyr Mytnyk 
500604ba230SVolodymyr Mytnyk 	prestera_acl_rule_keymask_pcl_id_set(&rule, 0);
501fa5d824cSVolodymyr Mytnyk 	ruleset = prestera_acl_ruleset_get(block->sw->acl, block,
502fa5d824cSVolodymyr Mytnyk 					   f->common.chain_index);
503604ba230SVolodymyr Mytnyk 	if (IS_ERR_OR_NULL(ruleset)) {
504604ba230SVolodymyr Mytnyk 		err = -EINVAL;
505604ba230SVolodymyr Mytnyk 		goto err_ruleset_get;
506604ba230SVolodymyr Mytnyk 	}
507604ba230SVolodymyr Mytnyk 
508604ba230SVolodymyr Mytnyk 	/* preserve keymask/template to this ruleset */
5099e6fd874SJiasheng Jiang 	err = prestera_acl_ruleset_keymask_set(ruleset, rule.re_key.match.mask);
5109e6fd874SJiasheng Jiang 	if (err)
5119e6fd874SJiasheng Jiang 		goto err_ruleset_keymask_set;
512604ba230SVolodymyr Mytnyk 
513604ba230SVolodymyr Mytnyk 	/* skip error, as it is not possible to reject template operation,
514604ba230SVolodymyr Mytnyk 	 * so, keep the reference to the ruleset for rules to be added
515604ba230SVolodymyr Mytnyk 	 * to that ruleset later. In case of offload fail, the ruleset
516604ba230SVolodymyr Mytnyk 	 * will be offloaded again during adding a new rule. Also,
517604ba230SVolodymyr Mytnyk 	 * unlikly possble that ruleset is already offloaded at this staage.
518604ba230SVolodymyr Mytnyk 	 */
519604ba230SVolodymyr Mytnyk 	prestera_acl_ruleset_offload(ruleset);
520604ba230SVolodymyr Mytnyk 
521604ba230SVolodymyr Mytnyk 	/* keep the reference to the ruleset */
522604ba230SVolodymyr Mytnyk 	template->ruleset = ruleset;
523fa5d824cSVolodymyr Mytnyk 	template->chain_index = f->common.chain_index;
524fa5d824cSVolodymyr Mytnyk 	list_add_rcu(&template->list, &block->template_list);
525604ba230SVolodymyr Mytnyk 	return 0;
526604ba230SVolodymyr Mytnyk 
5279e6fd874SJiasheng Jiang err_ruleset_keymask_set:
5289e6fd874SJiasheng Jiang 	prestera_acl_ruleset_put(ruleset);
529604ba230SVolodymyr Mytnyk err_ruleset_get:
530604ba230SVolodymyr Mytnyk 	kfree(template);
531604ba230SVolodymyr Mytnyk err_malloc:
532604ba230SVolodymyr Mytnyk 	NL_SET_ERR_MSG_MOD(f->common.extack, "Create chain template failed");
533604ba230SVolodymyr Mytnyk 	return err;
534604ba230SVolodymyr Mytnyk }
535604ba230SVolodymyr Mytnyk 
prestera_flower_tmplt_destroy(struct prestera_flow_block * block,struct flow_cls_offload * f)536604ba230SVolodymyr Mytnyk void prestera_flower_tmplt_destroy(struct prestera_flow_block *block,
537604ba230SVolodymyr Mytnyk 				   struct flow_cls_offload *f)
538604ba230SVolodymyr Mytnyk {
539b3ae2d35SVolodymyr Mytnyk 	struct prestera_flower_template *template, *tmp;
540b3ae2d35SVolodymyr Mytnyk 
541b3ae2d35SVolodymyr Mytnyk 	list_for_each_entry_safe(template, tmp, &block->template_list, list)
542b3ae2d35SVolodymyr Mytnyk 		if (template->chain_index == f->common.chain_index) {
543b3ae2d35SVolodymyr Mytnyk 			/* put the reference to the ruleset kept in create */
544b3ae2d35SVolodymyr Mytnyk 			prestera_flower_template_free(template);
545b3ae2d35SVolodymyr Mytnyk 			return;
546b3ae2d35SVolodymyr Mytnyk 		}
547604ba230SVolodymyr Mytnyk }
548604ba230SVolodymyr Mytnyk 
prestera_flower_stats(struct prestera_flow_block * block,struct flow_cls_offload * f)5498b474a9fSSerhiy Boiko int prestera_flower_stats(struct prestera_flow_block *block,
5508b474a9fSSerhiy Boiko 			  struct flow_cls_offload *f)
5518b474a9fSSerhiy Boiko {
55247327e19SVolodymyr Mytnyk 	struct prestera_acl_ruleset *ruleset;
5538b474a9fSSerhiy Boiko 	struct prestera_acl_rule *rule;
5548b474a9fSSerhiy Boiko 	u64 packets;
5558b474a9fSSerhiy Boiko 	u64 lastuse;
5568b474a9fSSerhiy Boiko 	u64 bytes;
5578b474a9fSSerhiy Boiko 	int err;
5588b474a9fSSerhiy Boiko 
559fa5d824cSVolodymyr Mytnyk 	ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block,
560fa5d824cSVolodymyr Mytnyk 					      f->common.chain_index);
56147327e19SVolodymyr Mytnyk 	if (IS_ERR(ruleset))
56247327e19SVolodymyr Mytnyk 		return PTR_ERR(ruleset);
5638b474a9fSSerhiy Boiko 
56447327e19SVolodymyr Mytnyk 	rule = prestera_acl_rule_lookup(ruleset, f->cookie);
56547327e19SVolodymyr Mytnyk 	if (!rule) {
56647327e19SVolodymyr Mytnyk 		err = -EINVAL;
56747327e19SVolodymyr Mytnyk 		goto err_rule_get_stats;
56847327e19SVolodymyr Mytnyk 	}
56947327e19SVolodymyr Mytnyk 
57047327e19SVolodymyr Mytnyk 	err = prestera_acl_rule_get_stats(block->sw->acl, rule, &packets,
57147327e19SVolodymyr Mytnyk 					  &bytes, &lastuse);
5728b474a9fSSerhiy Boiko 	if (err)
57347327e19SVolodymyr Mytnyk 		goto err_rule_get_stats;
5748b474a9fSSerhiy Boiko 
5758b474a9fSSerhiy Boiko 	flow_stats_update(&f->stats, bytes, packets, 0, lastuse,
57647327e19SVolodymyr Mytnyk 			  FLOW_ACTION_HW_STATS_DELAYED);
57747327e19SVolodymyr Mytnyk 
57847327e19SVolodymyr Mytnyk err_rule_get_stats:
57947327e19SVolodymyr Mytnyk 	prestera_acl_ruleset_put(ruleset);
58047327e19SVolodymyr Mytnyk 	return err;
5818b474a9fSSerhiy Boiko }
582