xref: /linux/drivers/net/ethernet/marvell/prestera/prestera_flower.c (revision 621cde16e49b3ecf7d59a8106a20aaebfb4a59a9)
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 &
2052b3082c6SRatheesh Kannoth 	    ~(BIT_ULL(FLOW_DISSECTOR_KEY_META) |
2062b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |
2072b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
2082b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
2092b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
2102b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
2112b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_ICMP) |
2122b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) |
2132b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_PORTS_RANGE) |
2142b3082c6SRatheesh 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;
232*f8a5ea8cSAsbjørn Sloth Tønnesen 
233*f8a5ea8cSAsbjørn Sloth Tønnesen 		if (flow_rule_has_control_flags(match.mask->flags,
234*f8a5ea8cSAsbjørn Sloth Tønnesen 						f->common.extack))
235*f8a5ea8cSAsbjørn Sloth Tønnesen 			return -EOPNOTSUPP;
2368b474a9fSSerhiy Boiko 	}
2378b474a9fSSerhiy Boiko 
2388b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_BASIC)) {
2398b474a9fSSerhiy Boiko 		struct flow_match_basic match;
2408b474a9fSSerhiy Boiko 
2418b474a9fSSerhiy Boiko 		flow_rule_match_basic(f_rule, &match);
24247327e19SVolodymyr Mytnyk 		n_proto_key = match.key->n_proto;
24347327e19SVolodymyr Mytnyk 		n_proto_mask = match.mask->n_proto;
2448b474a9fSSerhiy Boiko 
24547327e19SVolodymyr Mytnyk 		if (ntohs(match.key->n_proto) == ETH_P_ALL) {
2468b474a9fSSerhiy Boiko 			n_proto_key = 0;
2478b474a9fSSerhiy Boiko 			n_proto_mask = 0;
2488b474a9fSSerhiy Boiko 		}
2498b474a9fSSerhiy Boiko 
25047327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, ETH_TYPE, n_proto_key);
25147327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, ETH_TYPE, n_proto_mask);
2528b474a9fSSerhiy Boiko 
25347327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, IP_PROTO, match.key->ip_proto);
25447327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, IP_PROTO, match.mask->ip_proto);
2558b474a9fSSerhiy Boiko 		ip_proto = match.key->ip_proto;
2568b474a9fSSerhiy Boiko 	}
2578b474a9fSSerhiy Boiko 
2588b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
2598b474a9fSSerhiy Boiko 		struct flow_match_eth_addrs match;
2608b474a9fSSerhiy Boiko 
2618b474a9fSSerhiy Boiko 		flow_rule_match_eth_addrs(f_rule, &match);
2628b474a9fSSerhiy Boiko 
26347327e19SVolodymyr Mytnyk 		/* DA key, mask */
26447327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->key,
26547327e19SVolodymyr Mytnyk 				 ETH_DMAC_0, &match.key->dst[0], 4);
26647327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->key,
26747327e19SVolodymyr Mytnyk 				 ETH_DMAC_1, &match.key->dst[4], 2);
2688b474a9fSSerhiy Boiko 
26947327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->mask,
27047327e19SVolodymyr Mytnyk 				 ETH_DMAC_0, &match.mask->dst[0], 4);
27147327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->mask,
27247327e19SVolodymyr Mytnyk 				 ETH_DMAC_1, &match.mask->dst[4], 2);
27347327e19SVolodymyr Mytnyk 
27447327e19SVolodymyr Mytnyk 		/* SA key, mask */
27547327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->key,
27647327e19SVolodymyr Mytnyk 				 ETH_SMAC_0, &match.key->src[0], 4);
27747327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->key,
27847327e19SVolodymyr Mytnyk 				 ETH_SMAC_1, &match.key->src[4], 2);
27947327e19SVolodymyr Mytnyk 
28047327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->mask,
28147327e19SVolodymyr Mytnyk 				 ETH_SMAC_0, &match.mask->src[0], 4);
28247327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->mask,
28347327e19SVolodymyr Mytnyk 				 ETH_SMAC_1, &match.mask->src[4], 2);
2848b474a9fSSerhiy Boiko 	}
2858b474a9fSSerhiy Boiko 
2868b474a9fSSerhiy Boiko 	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
2878b474a9fSSerhiy Boiko 		struct flow_match_ipv4_addrs match;
2888b474a9fSSerhiy Boiko 
2898b474a9fSSerhiy Boiko 		flow_rule_match_ipv4_addrs(f_rule, &match);
2908b474a9fSSerhiy Boiko 
29147327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, IP_SRC, match.key->src);
29247327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, IP_SRC, match.mask->src);
2938b474a9fSSerhiy Boiko 
29447327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, IP_DST, match.key->dst);
29547327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, IP_DST, match.mask->dst);
2968b474a9fSSerhiy Boiko 	}
2978b474a9fSSerhiy Boiko 
2988b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_PORTS)) {
2998b474a9fSSerhiy Boiko 		struct flow_match_ports match;
3008b474a9fSSerhiy Boiko 
3018b474a9fSSerhiy Boiko 		if (ip_proto != IPPROTO_TCP && ip_proto != IPPROTO_UDP) {
3028b474a9fSSerhiy Boiko 			NL_SET_ERR_MSG_MOD
3038b474a9fSSerhiy Boiko 			    (f->common.extack,
3048b474a9fSSerhiy Boiko 			     "Only UDP and TCP keys are supported");
3058b474a9fSSerhiy Boiko 			return -EINVAL;
3068b474a9fSSerhiy Boiko 		}
3078b474a9fSSerhiy Boiko 
3088b474a9fSSerhiy Boiko 		flow_rule_match_ports(f_rule, &match);
3098b474a9fSSerhiy Boiko 
31047327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, L4_PORT_SRC, match.key->src);
31147327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, L4_PORT_SRC, match.mask->src);
3128b474a9fSSerhiy Boiko 
31347327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, L4_PORT_DST, match.key->dst);
31447327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, L4_PORT_DST, match.mask->dst);
3158b474a9fSSerhiy Boiko 	}
3168b474a9fSSerhiy Boiko 
317551871bfSMaksym Glubokiy 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_PORTS_RANGE)) {
318551871bfSMaksym Glubokiy 		struct flow_match_ports_range match;
319551871bfSMaksym Glubokiy 		__be32 tp_key, tp_mask;
320551871bfSMaksym Glubokiy 
321551871bfSMaksym Glubokiy 		flow_rule_match_ports_range(f_rule, &match);
322551871bfSMaksym Glubokiy 
323551871bfSMaksym Glubokiy 		/* src port range (min, max) */
324551871bfSMaksym Glubokiy 		tp_key = htonl(ntohs(match.key->tp_min.src) |
325551871bfSMaksym Glubokiy 			       (ntohs(match.key->tp_max.src) << 16));
326551871bfSMaksym Glubokiy 		tp_mask = htonl(ntohs(match.mask->tp_min.src) |
327551871bfSMaksym Glubokiy 				(ntohs(match.mask->tp_max.src) << 16));
328551871bfSMaksym Glubokiy 		rule_match_set(r_match->key, L4_PORT_RANGE_SRC, tp_key);
329551871bfSMaksym Glubokiy 		rule_match_set(r_match->mask, L4_PORT_RANGE_SRC, tp_mask);
330551871bfSMaksym Glubokiy 
331551871bfSMaksym Glubokiy 		/* dst port range (min, max) */
332551871bfSMaksym Glubokiy 		tp_key = htonl(ntohs(match.key->tp_min.dst) |
333551871bfSMaksym Glubokiy 			       (ntohs(match.key->tp_max.dst) << 16));
334551871bfSMaksym Glubokiy 		tp_mask = htonl(ntohs(match.mask->tp_min.dst) |
335551871bfSMaksym Glubokiy 				(ntohs(match.mask->tp_max.dst) << 16));
336551871bfSMaksym Glubokiy 		rule_match_set(r_match->key, L4_PORT_RANGE_DST, tp_key);
337551871bfSMaksym Glubokiy 		rule_match_set(r_match->mask, L4_PORT_RANGE_DST, tp_mask);
338551871bfSMaksym Glubokiy 	}
339551871bfSMaksym Glubokiy 
3408b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_VLAN)) {
3418b474a9fSSerhiy Boiko 		struct flow_match_vlan match;
3428b474a9fSSerhiy Boiko 
3438b474a9fSSerhiy Boiko 		flow_rule_match_vlan(f_rule, &match);
3448b474a9fSSerhiy Boiko 
3458b474a9fSSerhiy Boiko 		if (match.mask->vlan_id != 0) {
34647327e19SVolodymyr Mytnyk 			__be16 key = cpu_to_be16(match.key->vlan_id);
34747327e19SVolodymyr Mytnyk 			__be16 mask = cpu_to_be16(match.mask->vlan_id);
34847327e19SVolodymyr Mytnyk 
34947327e19SVolodymyr Mytnyk 			rule_match_set(r_match->key, VLAN_ID, key);
35047327e19SVolodymyr Mytnyk 			rule_match_set(r_match->mask, VLAN_ID, mask);
3518b474a9fSSerhiy Boiko 		}
3528b474a9fSSerhiy Boiko 
35347327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, VLAN_TPID, match.key->vlan_tpid);
35447327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, VLAN_TPID, match.mask->vlan_tpid);
3558b474a9fSSerhiy Boiko 	}
3568b474a9fSSerhiy Boiko 
3578b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_ICMP)) {
3588b474a9fSSerhiy Boiko 		struct flow_match_icmp match;
3598b474a9fSSerhiy Boiko 
3608b474a9fSSerhiy Boiko 		flow_rule_match_icmp(f_rule, &match);
3618b474a9fSSerhiy Boiko 
36247327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, ICMP_TYPE, match.key->type);
36347327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, ICMP_TYPE, match.mask->type);
3648b474a9fSSerhiy Boiko 
36547327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, ICMP_CODE, match.key->code);
36647327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, ICMP_CODE, match.mask->code);
3678b474a9fSSerhiy Boiko 	}
3688b474a9fSSerhiy Boiko 
36947327e19SVolodymyr Mytnyk 	return prestera_flower_parse_actions(block, rule, &f->rule->action,
370fa5d824cSVolodymyr Mytnyk 					     f->common.chain_index,
3718b474a9fSSerhiy Boiko 					     f->common.extack);
3728b474a9fSSerhiy Boiko }
3738b474a9fSSerhiy Boiko 
prestera_flower_prio_check(struct prestera_flow_block * block,struct flow_cls_offload * f)37444af9571SMaksym Glubokiy static int prestera_flower_prio_check(struct prestera_flow_block *block,
37544af9571SMaksym Glubokiy 				      struct flow_cls_offload *f)
37644af9571SMaksym Glubokiy {
37744af9571SMaksym Glubokiy 	u32 mall_prio_min;
37844af9571SMaksym Glubokiy 	u32 mall_prio_max;
37944af9571SMaksym Glubokiy 	int err;
38044af9571SMaksym Glubokiy 
38144af9571SMaksym Glubokiy 	err = prestera_mall_prio_get(block, &mall_prio_min, &mall_prio_max);
38244af9571SMaksym Glubokiy 	if (err == -ENOENT)
38344af9571SMaksym Glubokiy 		/* No matchall filters installed on this chain. */
38444af9571SMaksym Glubokiy 		return 0;
38544af9571SMaksym Glubokiy 
38644af9571SMaksym Glubokiy 	if (err) {
38744af9571SMaksym Glubokiy 		NL_SET_ERR_MSG(f->common.extack, "Failed to get matchall priorities");
38844af9571SMaksym Glubokiy 		return err;
38944af9571SMaksym Glubokiy 	}
39044af9571SMaksym Glubokiy 
39144af9571SMaksym Glubokiy 	if (f->common.prio <= mall_prio_max && block->ingress) {
39244af9571SMaksym Glubokiy 		NL_SET_ERR_MSG(f->common.extack,
39344af9571SMaksym Glubokiy 			       "Failed to add in front of existing matchall rules");
39444af9571SMaksym Glubokiy 		return -EOPNOTSUPP;
39544af9571SMaksym Glubokiy 	}
39644af9571SMaksym Glubokiy 	if (f->common.prio >= mall_prio_min && !block->ingress) {
39744af9571SMaksym Glubokiy 		NL_SET_ERR_MSG(f->common.extack, "Failed to add behind of existing matchall rules");
39844af9571SMaksym Glubokiy 		return -EOPNOTSUPP;
39944af9571SMaksym Glubokiy 	}
40044af9571SMaksym Glubokiy 
40144af9571SMaksym Glubokiy 	return 0;
40244af9571SMaksym Glubokiy }
40344af9571SMaksym Glubokiy 
prestera_flower_prio_get(struct prestera_flow_block * block,u32 chain_index,u32 * prio_min,u32 * prio_max)40444af9571SMaksym Glubokiy int prestera_flower_prio_get(struct prestera_flow_block *block, u32 chain_index,
40544af9571SMaksym Glubokiy 			     u32 *prio_min, u32 *prio_max)
40644af9571SMaksym Glubokiy {
40744af9571SMaksym Glubokiy 	struct prestera_acl_ruleset *ruleset;
40844af9571SMaksym Glubokiy 
40944af9571SMaksym Glubokiy 	ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block, chain_index);
41044af9571SMaksym Glubokiy 	if (IS_ERR(ruleset))
41144af9571SMaksym Glubokiy 		return PTR_ERR(ruleset);
41244af9571SMaksym Glubokiy 
41344af9571SMaksym Glubokiy 	prestera_acl_ruleset_prio_get(ruleset, prio_min, prio_max);
41444af9571SMaksym Glubokiy 	return 0;
41544af9571SMaksym Glubokiy }
41644af9571SMaksym Glubokiy 
prestera_flower_replace(struct prestera_flow_block * block,struct flow_cls_offload * f)4178b474a9fSSerhiy Boiko int prestera_flower_replace(struct prestera_flow_block *block,
4188b474a9fSSerhiy Boiko 			    struct flow_cls_offload *f)
4198b474a9fSSerhiy Boiko {
42047327e19SVolodymyr Mytnyk 	struct prestera_acl_ruleset *ruleset;
42147327e19SVolodymyr Mytnyk 	struct prestera_acl *acl = block->sw->acl;
4228b474a9fSSerhiy Boiko 	struct prestera_acl_rule *rule;
4238b474a9fSSerhiy Boiko 	int err;
4248b474a9fSSerhiy Boiko 
42544af9571SMaksym Glubokiy 	err = prestera_flower_prio_check(block, f);
42644af9571SMaksym Glubokiy 	if (err)
42744af9571SMaksym Glubokiy 		return err;
42844af9571SMaksym Glubokiy 
429fa5d824cSVolodymyr Mytnyk 	ruleset = prestera_acl_ruleset_get(acl, block, f->common.chain_index);
43047327e19SVolodymyr Mytnyk 	if (IS_ERR(ruleset))
43147327e19SVolodymyr Mytnyk 		return PTR_ERR(ruleset);
43247327e19SVolodymyr Mytnyk 
43347327e19SVolodymyr Mytnyk 	/* increments the ruleset reference */
434fa5d824cSVolodymyr Mytnyk 	rule = prestera_acl_rule_create(ruleset, f->cookie,
435fa5d824cSVolodymyr Mytnyk 					f->common.chain_index);
43647327e19SVolodymyr Mytnyk 	if (IS_ERR(rule)) {
43747327e19SVolodymyr Mytnyk 		err = PTR_ERR(rule);
43847327e19SVolodymyr Mytnyk 		goto err_rule_create;
43947327e19SVolodymyr Mytnyk 	}
4408b474a9fSSerhiy Boiko 
4418b474a9fSSerhiy Boiko 	err = prestera_flower_parse(block, rule, f);
4428b474a9fSSerhiy Boiko 	if (err)
44347327e19SVolodymyr Mytnyk 		goto err_rule_add;
4448b474a9fSSerhiy Boiko 
44547327e19SVolodymyr Mytnyk 	if (!prestera_acl_ruleset_is_offload(ruleset)) {
44647327e19SVolodymyr Mytnyk 		err = prestera_acl_ruleset_offload(ruleset);
44747327e19SVolodymyr Mytnyk 		if (err)
44847327e19SVolodymyr Mytnyk 			goto err_ruleset_offload;
44947327e19SVolodymyr Mytnyk 	}
45047327e19SVolodymyr Mytnyk 
45147327e19SVolodymyr Mytnyk 	err = prestera_acl_rule_add(block->sw, rule);
4528b474a9fSSerhiy Boiko 	if (err)
4538b474a9fSSerhiy Boiko 		goto err_rule_add;
4548b474a9fSSerhiy Boiko 
45547327e19SVolodymyr Mytnyk 	prestera_acl_ruleset_put(ruleset);
4568b474a9fSSerhiy Boiko 	return 0;
4578b474a9fSSerhiy Boiko 
45847327e19SVolodymyr Mytnyk err_ruleset_offload:
4598b474a9fSSerhiy Boiko err_rule_add:
4608b474a9fSSerhiy Boiko 	prestera_acl_rule_destroy(rule);
46147327e19SVolodymyr Mytnyk err_rule_create:
46247327e19SVolodymyr Mytnyk 	prestera_acl_ruleset_put(ruleset);
4638b474a9fSSerhiy Boiko 	return err;
4648b474a9fSSerhiy Boiko }
4658b474a9fSSerhiy Boiko 
prestera_flower_destroy(struct prestera_flow_block * block,struct flow_cls_offload * f)4668b474a9fSSerhiy Boiko void prestera_flower_destroy(struct prestera_flow_block *block,
4678b474a9fSSerhiy Boiko 			     struct flow_cls_offload *f)
4688b474a9fSSerhiy Boiko {
46947327e19SVolodymyr Mytnyk 	struct prestera_acl_ruleset *ruleset;
4708b474a9fSSerhiy Boiko 	struct prestera_acl_rule *rule;
4718b474a9fSSerhiy Boiko 
472fa5d824cSVolodymyr Mytnyk 	ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block,
473fa5d824cSVolodymyr Mytnyk 					      f->common.chain_index);
47447327e19SVolodymyr Mytnyk 	if (IS_ERR(ruleset))
47547327e19SVolodymyr Mytnyk 		return;
47647327e19SVolodymyr Mytnyk 
47747327e19SVolodymyr Mytnyk 	rule = prestera_acl_rule_lookup(ruleset, f->cookie);
4788b474a9fSSerhiy Boiko 	if (rule) {
47947327e19SVolodymyr Mytnyk 		prestera_acl_rule_del(block->sw, rule);
4808b474a9fSSerhiy Boiko 		prestera_acl_rule_destroy(rule);
4818b474a9fSSerhiy Boiko 	}
48247327e19SVolodymyr Mytnyk 	prestera_acl_ruleset_put(ruleset);
4838b474a9fSSerhiy Boiko }
4848b474a9fSSerhiy Boiko 
prestera_flower_tmplt_create(struct prestera_flow_block * block,struct flow_cls_offload * f)485604ba230SVolodymyr Mytnyk int prestera_flower_tmplt_create(struct prestera_flow_block *block,
486604ba230SVolodymyr Mytnyk 				 struct flow_cls_offload *f)
487604ba230SVolodymyr Mytnyk {
488604ba230SVolodymyr Mytnyk 	struct prestera_flower_template *template;
489604ba230SVolodymyr Mytnyk 	struct prestera_acl_ruleset *ruleset;
490604ba230SVolodymyr Mytnyk 	struct prestera_acl_rule rule;
491604ba230SVolodymyr Mytnyk 	int err;
492604ba230SVolodymyr Mytnyk 
493604ba230SVolodymyr Mytnyk 	memset(&rule, 0, sizeof(rule));
494604ba230SVolodymyr Mytnyk 	err = prestera_flower_parse(block, &rule, f);
495604ba230SVolodymyr Mytnyk 	if (err)
496604ba230SVolodymyr Mytnyk 		return err;
497604ba230SVolodymyr Mytnyk 
498604ba230SVolodymyr Mytnyk 	template = kmalloc(sizeof(*template), GFP_KERNEL);
499604ba230SVolodymyr Mytnyk 	if (!template) {
500604ba230SVolodymyr Mytnyk 		err = -ENOMEM;
501604ba230SVolodymyr Mytnyk 		goto err_malloc;
502604ba230SVolodymyr Mytnyk 	}
503604ba230SVolodymyr Mytnyk 
504604ba230SVolodymyr Mytnyk 	prestera_acl_rule_keymask_pcl_id_set(&rule, 0);
505fa5d824cSVolodymyr Mytnyk 	ruleset = prestera_acl_ruleset_get(block->sw->acl, block,
506fa5d824cSVolodymyr Mytnyk 					   f->common.chain_index);
507604ba230SVolodymyr Mytnyk 	if (IS_ERR_OR_NULL(ruleset)) {
508604ba230SVolodymyr Mytnyk 		err = -EINVAL;
509604ba230SVolodymyr Mytnyk 		goto err_ruleset_get;
510604ba230SVolodymyr Mytnyk 	}
511604ba230SVolodymyr Mytnyk 
512604ba230SVolodymyr Mytnyk 	/* preserve keymask/template to this ruleset */
5139e6fd874SJiasheng Jiang 	err = prestera_acl_ruleset_keymask_set(ruleset, rule.re_key.match.mask);
5149e6fd874SJiasheng Jiang 	if (err)
5159e6fd874SJiasheng Jiang 		goto err_ruleset_keymask_set;
516604ba230SVolodymyr Mytnyk 
517604ba230SVolodymyr Mytnyk 	/* skip error, as it is not possible to reject template operation,
518604ba230SVolodymyr Mytnyk 	 * so, keep the reference to the ruleset for rules to be added
519604ba230SVolodymyr Mytnyk 	 * to that ruleset later. In case of offload fail, the ruleset
520604ba230SVolodymyr Mytnyk 	 * will be offloaded again during adding a new rule. Also,
521604ba230SVolodymyr Mytnyk 	 * unlikly possble that ruleset is already offloaded at this staage.
522604ba230SVolodymyr Mytnyk 	 */
523604ba230SVolodymyr Mytnyk 	prestera_acl_ruleset_offload(ruleset);
524604ba230SVolodymyr Mytnyk 
525604ba230SVolodymyr Mytnyk 	/* keep the reference to the ruleset */
526604ba230SVolodymyr Mytnyk 	template->ruleset = ruleset;
527fa5d824cSVolodymyr Mytnyk 	template->chain_index = f->common.chain_index;
528fa5d824cSVolodymyr Mytnyk 	list_add_rcu(&template->list, &block->template_list);
529604ba230SVolodymyr Mytnyk 	return 0;
530604ba230SVolodymyr Mytnyk 
5319e6fd874SJiasheng Jiang err_ruleset_keymask_set:
5329e6fd874SJiasheng Jiang 	prestera_acl_ruleset_put(ruleset);
533604ba230SVolodymyr Mytnyk err_ruleset_get:
534604ba230SVolodymyr Mytnyk 	kfree(template);
535604ba230SVolodymyr Mytnyk err_malloc:
536604ba230SVolodymyr Mytnyk 	NL_SET_ERR_MSG_MOD(f->common.extack, "Create chain template failed");
537604ba230SVolodymyr Mytnyk 	return err;
538604ba230SVolodymyr Mytnyk }
539604ba230SVolodymyr Mytnyk 
prestera_flower_tmplt_destroy(struct prestera_flow_block * block,struct flow_cls_offload * f)540604ba230SVolodymyr Mytnyk void prestera_flower_tmplt_destroy(struct prestera_flow_block *block,
541604ba230SVolodymyr Mytnyk 				   struct flow_cls_offload *f)
542604ba230SVolodymyr Mytnyk {
543b3ae2d35SVolodymyr Mytnyk 	struct prestera_flower_template *template, *tmp;
544b3ae2d35SVolodymyr Mytnyk 
545b3ae2d35SVolodymyr Mytnyk 	list_for_each_entry_safe(template, tmp, &block->template_list, list)
546b3ae2d35SVolodymyr Mytnyk 		if (template->chain_index == f->common.chain_index) {
547b3ae2d35SVolodymyr Mytnyk 			/* put the reference to the ruleset kept in create */
548b3ae2d35SVolodymyr Mytnyk 			prestera_flower_template_free(template);
549b3ae2d35SVolodymyr Mytnyk 			return;
550b3ae2d35SVolodymyr Mytnyk 		}
551604ba230SVolodymyr Mytnyk }
552604ba230SVolodymyr Mytnyk 
prestera_flower_stats(struct prestera_flow_block * block,struct flow_cls_offload * f)5538b474a9fSSerhiy Boiko int prestera_flower_stats(struct prestera_flow_block *block,
5548b474a9fSSerhiy Boiko 			  struct flow_cls_offload *f)
5558b474a9fSSerhiy Boiko {
55647327e19SVolodymyr Mytnyk 	struct prestera_acl_ruleset *ruleset;
5578b474a9fSSerhiy Boiko 	struct prestera_acl_rule *rule;
5588b474a9fSSerhiy Boiko 	u64 packets;
5598b474a9fSSerhiy Boiko 	u64 lastuse;
5608b474a9fSSerhiy Boiko 	u64 bytes;
5618b474a9fSSerhiy Boiko 	int err;
5628b474a9fSSerhiy Boiko 
563fa5d824cSVolodymyr Mytnyk 	ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block,
564fa5d824cSVolodymyr Mytnyk 					      f->common.chain_index);
56547327e19SVolodymyr Mytnyk 	if (IS_ERR(ruleset))
56647327e19SVolodymyr Mytnyk 		return PTR_ERR(ruleset);
5678b474a9fSSerhiy Boiko 
56847327e19SVolodymyr Mytnyk 	rule = prestera_acl_rule_lookup(ruleset, f->cookie);
56947327e19SVolodymyr Mytnyk 	if (!rule) {
57047327e19SVolodymyr Mytnyk 		err = -EINVAL;
57147327e19SVolodymyr Mytnyk 		goto err_rule_get_stats;
57247327e19SVolodymyr Mytnyk 	}
57347327e19SVolodymyr Mytnyk 
57447327e19SVolodymyr Mytnyk 	err = prestera_acl_rule_get_stats(block->sw->acl, rule, &packets,
57547327e19SVolodymyr Mytnyk 					  &bytes, &lastuse);
5768b474a9fSSerhiy Boiko 	if (err)
57747327e19SVolodymyr Mytnyk 		goto err_rule_get_stats;
5788b474a9fSSerhiy Boiko 
5798b474a9fSSerhiy Boiko 	flow_stats_update(&f->stats, bytes, packets, 0, lastuse,
58047327e19SVolodymyr Mytnyk 			  FLOW_ACTION_HW_STATS_DELAYED);
58147327e19SVolodymyr Mytnyk 
58247327e19SVolodymyr Mytnyk err_rule_get_stats:
58347327e19SVolodymyr Mytnyk 	prestera_acl_ruleset_put(ruleset);
58447327e19SVolodymyr Mytnyk 	return err;
5858b474a9fSSerhiy Boiko }
586