xref: /linux/drivers/net/ethernet/marvell/prestera/prestera_flower.c (revision fa5d824ce5dd8306c66f45c34fd78536e6ce2488)
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"
88b474a9fSSerhiy Boiko 
9604ba230SVolodymyr Mytnyk struct prestera_flower_template {
10604ba230SVolodymyr Mytnyk 	struct prestera_acl_ruleset *ruleset;
11*fa5d824cSVolodymyr Mytnyk 	struct list_head list;
12*fa5d824cSVolodymyr Mytnyk 	u32 chain_index;
13604ba230SVolodymyr Mytnyk };
14604ba230SVolodymyr Mytnyk 
15604ba230SVolodymyr Mytnyk void prestera_flower_template_cleanup(struct prestera_flow_block *block)
16604ba230SVolodymyr Mytnyk {
17*fa5d824cSVolodymyr Mytnyk 	struct prestera_flower_template *template;
18*fa5d824cSVolodymyr Mytnyk 	struct list_head *pos, *n;
19*fa5d824cSVolodymyr Mytnyk 
20*fa5d824cSVolodymyr Mytnyk 	/* put the reference to all rulesets kept in tmpl create */
21*fa5d824cSVolodymyr Mytnyk 	list_for_each_safe(pos, n, &block->template_list) {
22*fa5d824cSVolodymyr Mytnyk 		template = list_entry(pos, typeof(*template), list);
23*fa5d824cSVolodymyr Mytnyk 		prestera_acl_ruleset_put(template->ruleset);
24*fa5d824cSVolodymyr Mytnyk 		list_del(&template->list);
25*fa5d824cSVolodymyr Mytnyk 		kfree(template);
26604ba230SVolodymyr Mytnyk 	}
27604ba230SVolodymyr Mytnyk }
28604ba230SVolodymyr Mytnyk 
29*fa5d824cSVolodymyr Mytnyk static int
30*fa5d824cSVolodymyr Mytnyk prestera_flower_parse_goto_action(struct prestera_flow_block *block,
31*fa5d824cSVolodymyr Mytnyk 				  struct prestera_acl_rule *rule,
32*fa5d824cSVolodymyr Mytnyk 				  u32 chain_index,
33*fa5d824cSVolodymyr Mytnyk 				  const struct flow_action_entry *act)
34*fa5d824cSVolodymyr Mytnyk {
35*fa5d824cSVolodymyr Mytnyk 	struct prestera_acl_ruleset *ruleset;
36*fa5d824cSVolodymyr Mytnyk 
37*fa5d824cSVolodymyr Mytnyk 	if (act->chain_index <= chain_index)
38*fa5d824cSVolodymyr Mytnyk 		/* we can jump only forward */
39*fa5d824cSVolodymyr Mytnyk 		return -EINVAL;
40*fa5d824cSVolodymyr Mytnyk 
41*fa5d824cSVolodymyr Mytnyk 	if (rule->re_arg.jump.valid)
42*fa5d824cSVolodymyr Mytnyk 		return -EEXIST;
43*fa5d824cSVolodymyr Mytnyk 
44*fa5d824cSVolodymyr Mytnyk 	ruleset = prestera_acl_ruleset_get(block->sw->acl, block,
45*fa5d824cSVolodymyr Mytnyk 					   act->chain_index);
46*fa5d824cSVolodymyr Mytnyk 	if (IS_ERR(ruleset))
47*fa5d824cSVolodymyr Mytnyk 		return PTR_ERR(ruleset);
48*fa5d824cSVolodymyr Mytnyk 
49*fa5d824cSVolodymyr Mytnyk 	rule->re_arg.jump.valid = 1;
50*fa5d824cSVolodymyr Mytnyk 	rule->re_arg.jump.i.index = prestera_acl_ruleset_index_get(ruleset);
51*fa5d824cSVolodymyr Mytnyk 
52*fa5d824cSVolodymyr Mytnyk 	rule->jump_ruleset = ruleset;
53*fa5d824cSVolodymyr Mytnyk 
54*fa5d824cSVolodymyr Mytnyk 	return 0;
55*fa5d824cSVolodymyr Mytnyk }
56*fa5d824cSVolodymyr Mytnyk 
578b474a9fSSerhiy Boiko static int prestera_flower_parse_actions(struct prestera_flow_block *block,
588b474a9fSSerhiy Boiko 					 struct prestera_acl_rule *rule,
598b474a9fSSerhiy Boiko 					 struct flow_action *flow_action,
60*fa5d824cSVolodymyr Mytnyk 					 u32 chain_index,
618b474a9fSSerhiy Boiko 					 struct netlink_ext_ack *extack)
628b474a9fSSerhiy Boiko {
638b474a9fSSerhiy Boiko 	const struct flow_action_entry *act;
64*fa5d824cSVolodymyr Mytnyk 	int err, i;
658b474a9fSSerhiy Boiko 
6647327e19SVolodymyr Mytnyk 	/* whole struct (rule->re_arg) must be initialized with 0 */
678b474a9fSSerhiy Boiko 	if (!flow_action_has_entries(flow_action))
688b474a9fSSerhiy Boiko 		return 0;
698b474a9fSSerhiy Boiko 
708b474a9fSSerhiy Boiko 	flow_action_for_each(i, act, flow_action) {
718b474a9fSSerhiy Boiko 		switch (act->id) {
728b474a9fSSerhiy Boiko 		case FLOW_ACTION_ACCEPT:
7347327e19SVolodymyr Mytnyk 			if (rule->re_arg.accept.valid)
7447327e19SVolodymyr Mytnyk 				return -EEXIST;
7547327e19SVolodymyr Mytnyk 
7647327e19SVolodymyr Mytnyk 			rule->re_arg.accept.valid = 1;
778b474a9fSSerhiy Boiko 			break;
788b474a9fSSerhiy Boiko 		case FLOW_ACTION_DROP:
7947327e19SVolodymyr Mytnyk 			if (rule->re_arg.drop.valid)
8047327e19SVolodymyr Mytnyk 				return -EEXIST;
8147327e19SVolodymyr Mytnyk 
8247327e19SVolodymyr Mytnyk 			rule->re_arg.drop.valid = 1;
838b474a9fSSerhiy Boiko 			break;
848b474a9fSSerhiy Boiko 		case FLOW_ACTION_TRAP:
8547327e19SVolodymyr Mytnyk 			if (rule->re_arg.trap.valid)
8647327e19SVolodymyr Mytnyk 				return -EEXIST;
8747327e19SVolodymyr Mytnyk 
8847327e19SVolodymyr Mytnyk 			rule->re_arg.trap.valid = 1;
898b474a9fSSerhiy Boiko 			break;
90*fa5d824cSVolodymyr Mytnyk 		case FLOW_ACTION_GOTO:
91*fa5d824cSVolodymyr Mytnyk 			err = prestera_flower_parse_goto_action(block, rule,
92*fa5d824cSVolodymyr Mytnyk 								chain_index,
93*fa5d824cSVolodymyr Mytnyk 								act);
94*fa5d824cSVolodymyr Mytnyk 			if (err)
95*fa5d824cSVolodymyr Mytnyk 				return err;
96*fa5d824cSVolodymyr Mytnyk 			break;
978b474a9fSSerhiy Boiko 		default:
988b474a9fSSerhiy Boiko 			NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
998b474a9fSSerhiy Boiko 			pr_err("Unsupported action\n");
1008b474a9fSSerhiy Boiko 			return -EOPNOTSUPP;
1018b474a9fSSerhiy Boiko 		}
1028b474a9fSSerhiy Boiko 	}
1038b474a9fSSerhiy Boiko 
1048b474a9fSSerhiy Boiko 	return 0;
1058b474a9fSSerhiy Boiko }
1068b474a9fSSerhiy Boiko 
1078b474a9fSSerhiy Boiko static int prestera_flower_parse_meta(struct prestera_acl_rule *rule,
1088b474a9fSSerhiy Boiko 				      struct flow_cls_offload *f,
1098b474a9fSSerhiy Boiko 				      struct prestera_flow_block *block)
11047327e19SVolodymyr Mytnyk {	struct flow_rule *f_rule = flow_cls_offload_flow_rule(f);
11147327e19SVolodymyr Mytnyk 	struct prestera_acl_match *r_match = &rule->re_key.match;
11247327e19SVolodymyr Mytnyk 	struct prestera_port *port;
1138b474a9fSSerhiy Boiko 	struct net_device *ingress_dev;
1148b474a9fSSerhiy Boiko 	struct flow_match_meta match;
11547327e19SVolodymyr Mytnyk 	__be16 key, mask;
1168b474a9fSSerhiy Boiko 
1178b474a9fSSerhiy Boiko 	flow_rule_match_meta(f_rule, &match);
1188b474a9fSSerhiy Boiko 	if (match.mask->ingress_ifindex != 0xFFFFFFFF) {
1198b474a9fSSerhiy Boiko 		NL_SET_ERR_MSG_MOD(f->common.extack,
1208b474a9fSSerhiy Boiko 				   "Unsupported ingress ifindex mask");
1218b474a9fSSerhiy Boiko 		return -EINVAL;
1228b474a9fSSerhiy Boiko 	}
1238b474a9fSSerhiy Boiko 
12447327e19SVolodymyr Mytnyk 	ingress_dev = __dev_get_by_index(block->net,
1258b474a9fSSerhiy Boiko 					 match.key->ingress_ifindex);
1268b474a9fSSerhiy Boiko 	if (!ingress_dev) {
1278b474a9fSSerhiy Boiko 		NL_SET_ERR_MSG_MOD(f->common.extack,
1288b474a9fSSerhiy Boiko 				   "Can't find specified ingress port to match on");
1298b474a9fSSerhiy Boiko 		return -EINVAL;
1308b474a9fSSerhiy Boiko 	}
1318b474a9fSSerhiy Boiko 
1328b474a9fSSerhiy Boiko 	if (!prestera_netdev_check(ingress_dev)) {
1338b474a9fSSerhiy Boiko 		NL_SET_ERR_MSG_MOD(f->common.extack,
1348b474a9fSSerhiy Boiko 				   "Can't match on switchdev ingress port");
1358b474a9fSSerhiy Boiko 		return -EINVAL;
1368b474a9fSSerhiy Boiko 	}
1378b474a9fSSerhiy Boiko 	port = netdev_priv(ingress_dev);
1388b474a9fSSerhiy Boiko 
13947327e19SVolodymyr Mytnyk 	mask = htons(0x1FFF);
14047327e19SVolodymyr Mytnyk 	key = htons(port->hw_id);
14147327e19SVolodymyr Mytnyk 	rule_match_set(r_match->key, SYS_PORT, key);
14247327e19SVolodymyr Mytnyk 	rule_match_set(r_match->mask, SYS_PORT, mask);
1438b474a9fSSerhiy Boiko 
14447327e19SVolodymyr Mytnyk 	mask = htons(0x1FF);
14547327e19SVolodymyr Mytnyk 	key = htons(port->dev_id);
14647327e19SVolodymyr Mytnyk 	rule_match_set(r_match->key, SYS_DEV, key);
14747327e19SVolodymyr Mytnyk 	rule_match_set(r_match->mask, SYS_DEV, mask);
14847327e19SVolodymyr Mytnyk 
14947327e19SVolodymyr Mytnyk 	return 0;
15047327e19SVolodymyr Mytnyk 
1518b474a9fSSerhiy Boiko }
1528b474a9fSSerhiy Boiko 
1538b474a9fSSerhiy Boiko static int prestera_flower_parse(struct prestera_flow_block *block,
1548b474a9fSSerhiy Boiko 				 struct prestera_acl_rule *rule,
1558b474a9fSSerhiy Boiko 				 struct flow_cls_offload *f)
15647327e19SVolodymyr Mytnyk {	struct flow_rule *f_rule = flow_cls_offload_flow_rule(f);
1578b474a9fSSerhiy Boiko 	struct flow_dissector *dissector = f_rule->match.dissector;
15847327e19SVolodymyr Mytnyk 	struct prestera_acl_match *r_match = &rule->re_key.match;
15947327e19SVolodymyr Mytnyk 	__be16 n_proto_mask = 0;
16047327e19SVolodymyr Mytnyk 	__be16 n_proto_key = 0;
1618b474a9fSSerhiy Boiko 	u16 addr_type = 0;
1628b474a9fSSerhiy Boiko 	u8 ip_proto = 0;
1638b474a9fSSerhiy Boiko 	int err;
1648b474a9fSSerhiy Boiko 
1658b474a9fSSerhiy Boiko 	if (dissector->used_keys &
1668b474a9fSSerhiy Boiko 	    ~(BIT(FLOW_DISSECTOR_KEY_META) |
1678b474a9fSSerhiy Boiko 	      BIT(FLOW_DISSECTOR_KEY_CONTROL) |
1688b474a9fSSerhiy Boiko 	      BIT(FLOW_DISSECTOR_KEY_BASIC) |
1698b474a9fSSerhiy Boiko 	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
1708b474a9fSSerhiy Boiko 	      BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
1718b474a9fSSerhiy Boiko 	      BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
1728b474a9fSSerhiy Boiko 	      BIT(FLOW_DISSECTOR_KEY_ICMP) |
1738b474a9fSSerhiy Boiko 	      BIT(FLOW_DISSECTOR_KEY_PORTS) |
1748b474a9fSSerhiy Boiko 	      BIT(FLOW_DISSECTOR_KEY_VLAN))) {
1758b474a9fSSerhiy Boiko 		NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported key");
1768b474a9fSSerhiy Boiko 		return -EOPNOTSUPP;
1778b474a9fSSerhiy Boiko 	}
1788b474a9fSSerhiy Boiko 
1798b474a9fSSerhiy Boiko 	prestera_acl_rule_priority_set(rule, f->common.prio);
1808b474a9fSSerhiy Boiko 
1818b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_META)) {
1828b474a9fSSerhiy Boiko 		err = prestera_flower_parse_meta(rule, f, block);
1838b474a9fSSerhiy Boiko 		if (err)
1848b474a9fSSerhiy Boiko 			return err;
1858b474a9fSSerhiy Boiko 	}
1868b474a9fSSerhiy Boiko 
1878b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_CONTROL)) {
1888b474a9fSSerhiy Boiko 		struct flow_match_control match;
1898b474a9fSSerhiy Boiko 
1908b474a9fSSerhiy Boiko 		flow_rule_match_control(f_rule, &match);
1918b474a9fSSerhiy Boiko 		addr_type = match.key->addr_type;
1928b474a9fSSerhiy Boiko 	}
1938b474a9fSSerhiy Boiko 
1948b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_BASIC)) {
1958b474a9fSSerhiy Boiko 		struct flow_match_basic match;
1968b474a9fSSerhiy Boiko 
1978b474a9fSSerhiy Boiko 		flow_rule_match_basic(f_rule, &match);
19847327e19SVolodymyr Mytnyk 		n_proto_key = match.key->n_proto;
19947327e19SVolodymyr Mytnyk 		n_proto_mask = match.mask->n_proto;
2008b474a9fSSerhiy Boiko 
20147327e19SVolodymyr Mytnyk 		if (ntohs(match.key->n_proto) == ETH_P_ALL) {
2028b474a9fSSerhiy Boiko 			n_proto_key = 0;
2038b474a9fSSerhiy Boiko 			n_proto_mask = 0;
2048b474a9fSSerhiy Boiko 		}
2058b474a9fSSerhiy Boiko 
20647327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, ETH_TYPE, n_proto_key);
20747327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, ETH_TYPE, n_proto_mask);
2088b474a9fSSerhiy Boiko 
20947327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, IP_PROTO, match.key->ip_proto);
21047327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, IP_PROTO, match.mask->ip_proto);
2118b474a9fSSerhiy Boiko 		ip_proto = match.key->ip_proto;
2128b474a9fSSerhiy Boiko 	}
2138b474a9fSSerhiy Boiko 
2148b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
2158b474a9fSSerhiy Boiko 		struct flow_match_eth_addrs match;
2168b474a9fSSerhiy Boiko 
2178b474a9fSSerhiy Boiko 		flow_rule_match_eth_addrs(f_rule, &match);
2188b474a9fSSerhiy Boiko 
21947327e19SVolodymyr Mytnyk 		/* DA key, mask */
22047327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->key,
22147327e19SVolodymyr Mytnyk 				 ETH_DMAC_0, &match.key->dst[0], 4);
22247327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->key,
22347327e19SVolodymyr Mytnyk 				 ETH_DMAC_1, &match.key->dst[4], 2);
2248b474a9fSSerhiy Boiko 
22547327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->mask,
22647327e19SVolodymyr Mytnyk 				 ETH_DMAC_0, &match.mask->dst[0], 4);
22747327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->mask,
22847327e19SVolodymyr Mytnyk 				 ETH_DMAC_1, &match.mask->dst[4], 2);
22947327e19SVolodymyr Mytnyk 
23047327e19SVolodymyr Mytnyk 		/* SA key, mask */
23147327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->key,
23247327e19SVolodymyr Mytnyk 				 ETH_SMAC_0, &match.key->src[0], 4);
23347327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->key,
23447327e19SVolodymyr Mytnyk 				 ETH_SMAC_1, &match.key->src[4], 2);
23547327e19SVolodymyr Mytnyk 
23647327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->mask,
23747327e19SVolodymyr Mytnyk 				 ETH_SMAC_0, &match.mask->src[0], 4);
23847327e19SVolodymyr Mytnyk 		rule_match_set_n(r_match->mask,
23947327e19SVolodymyr Mytnyk 				 ETH_SMAC_1, &match.mask->src[4], 2);
2408b474a9fSSerhiy Boiko 	}
2418b474a9fSSerhiy Boiko 
2428b474a9fSSerhiy Boiko 	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
2438b474a9fSSerhiy Boiko 		struct flow_match_ipv4_addrs match;
2448b474a9fSSerhiy Boiko 
2458b474a9fSSerhiy Boiko 		flow_rule_match_ipv4_addrs(f_rule, &match);
2468b474a9fSSerhiy Boiko 
24747327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, IP_SRC, match.key->src);
24847327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, IP_SRC, match.mask->src);
2498b474a9fSSerhiy Boiko 
25047327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, IP_DST, match.key->dst);
25147327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, IP_DST, match.mask->dst);
2528b474a9fSSerhiy Boiko 	}
2538b474a9fSSerhiy Boiko 
2548b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_PORTS)) {
2558b474a9fSSerhiy Boiko 		struct flow_match_ports match;
2568b474a9fSSerhiy Boiko 
2578b474a9fSSerhiy Boiko 		if (ip_proto != IPPROTO_TCP && ip_proto != IPPROTO_UDP) {
2588b474a9fSSerhiy Boiko 			NL_SET_ERR_MSG_MOD
2598b474a9fSSerhiy Boiko 			    (f->common.extack,
2608b474a9fSSerhiy Boiko 			     "Only UDP and TCP keys are supported");
2618b474a9fSSerhiy Boiko 			return -EINVAL;
2628b474a9fSSerhiy Boiko 		}
2638b474a9fSSerhiy Boiko 
2648b474a9fSSerhiy Boiko 		flow_rule_match_ports(f_rule, &match);
2658b474a9fSSerhiy Boiko 
26647327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, L4_PORT_SRC, match.key->src);
26747327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, L4_PORT_SRC, match.mask->src);
2688b474a9fSSerhiy Boiko 
26947327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, L4_PORT_DST, match.key->dst);
27047327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, L4_PORT_DST, match.mask->dst);
2718b474a9fSSerhiy Boiko 	}
2728b474a9fSSerhiy Boiko 
2738b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_VLAN)) {
2748b474a9fSSerhiy Boiko 		struct flow_match_vlan match;
2758b474a9fSSerhiy Boiko 
2768b474a9fSSerhiy Boiko 		flow_rule_match_vlan(f_rule, &match);
2778b474a9fSSerhiy Boiko 
2788b474a9fSSerhiy Boiko 		if (match.mask->vlan_id != 0) {
27947327e19SVolodymyr Mytnyk 			__be16 key = cpu_to_be16(match.key->vlan_id);
28047327e19SVolodymyr Mytnyk 			__be16 mask = cpu_to_be16(match.mask->vlan_id);
28147327e19SVolodymyr Mytnyk 
28247327e19SVolodymyr Mytnyk 			rule_match_set(r_match->key, VLAN_ID, key);
28347327e19SVolodymyr Mytnyk 			rule_match_set(r_match->mask, VLAN_ID, mask);
2848b474a9fSSerhiy Boiko 		}
2858b474a9fSSerhiy Boiko 
28647327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, VLAN_TPID, match.key->vlan_tpid);
28747327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, VLAN_TPID, match.mask->vlan_tpid);
2888b474a9fSSerhiy Boiko 	}
2898b474a9fSSerhiy Boiko 
2908b474a9fSSerhiy Boiko 	if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_ICMP)) {
2918b474a9fSSerhiy Boiko 		struct flow_match_icmp match;
2928b474a9fSSerhiy Boiko 
2938b474a9fSSerhiy Boiko 		flow_rule_match_icmp(f_rule, &match);
2948b474a9fSSerhiy Boiko 
29547327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, ICMP_TYPE, match.key->type);
29647327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, ICMP_TYPE, match.mask->type);
2978b474a9fSSerhiy Boiko 
29847327e19SVolodymyr Mytnyk 		rule_match_set(r_match->key, ICMP_CODE, match.key->code);
29947327e19SVolodymyr Mytnyk 		rule_match_set(r_match->mask, ICMP_CODE, match.mask->code);
3008b474a9fSSerhiy Boiko 	}
3018b474a9fSSerhiy Boiko 
30247327e19SVolodymyr Mytnyk 	return prestera_flower_parse_actions(block, rule, &f->rule->action,
303*fa5d824cSVolodymyr Mytnyk 					     f->common.chain_index,
3048b474a9fSSerhiy Boiko 					     f->common.extack);
3058b474a9fSSerhiy Boiko }
3068b474a9fSSerhiy Boiko 
3078b474a9fSSerhiy Boiko int prestera_flower_replace(struct prestera_flow_block *block,
3088b474a9fSSerhiy Boiko 			    struct flow_cls_offload *f)
3098b474a9fSSerhiy Boiko {
31047327e19SVolodymyr Mytnyk 	struct prestera_acl_ruleset *ruleset;
31147327e19SVolodymyr Mytnyk 	struct prestera_acl *acl = block->sw->acl;
3128b474a9fSSerhiy Boiko 	struct prestera_acl_rule *rule;
3138b474a9fSSerhiy Boiko 	int err;
3148b474a9fSSerhiy Boiko 
315*fa5d824cSVolodymyr Mytnyk 	ruleset = prestera_acl_ruleset_get(acl, block, f->common.chain_index);
31647327e19SVolodymyr Mytnyk 	if (IS_ERR(ruleset))
31747327e19SVolodymyr Mytnyk 		return PTR_ERR(ruleset);
31847327e19SVolodymyr Mytnyk 
31947327e19SVolodymyr Mytnyk 	/* increments the ruleset reference */
320*fa5d824cSVolodymyr Mytnyk 	rule = prestera_acl_rule_create(ruleset, f->cookie,
321*fa5d824cSVolodymyr Mytnyk 					f->common.chain_index);
32247327e19SVolodymyr Mytnyk 	if (IS_ERR(rule)) {
32347327e19SVolodymyr Mytnyk 		err = PTR_ERR(rule);
32447327e19SVolodymyr Mytnyk 		goto err_rule_create;
32547327e19SVolodymyr Mytnyk 	}
3268b474a9fSSerhiy Boiko 
3278b474a9fSSerhiy Boiko 	err = prestera_flower_parse(block, rule, f);
3288b474a9fSSerhiy Boiko 	if (err)
32947327e19SVolodymyr Mytnyk 		goto err_rule_add;
3308b474a9fSSerhiy Boiko 
33147327e19SVolodymyr Mytnyk 	if (!prestera_acl_ruleset_is_offload(ruleset)) {
33247327e19SVolodymyr Mytnyk 		err = prestera_acl_ruleset_offload(ruleset);
33347327e19SVolodymyr Mytnyk 		if (err)
33447327e19SVolodymyr Mytnyk 			goto err_ruleset_offload;
33547327e19SVolodymyr Mytnyk 	}
33647327e19SVolodymyr Mytnyk 
33747327e19SVolodymyr Mytnyk 	err = prestera_acl_rule_add(block->sw, rule);
3388b474a9fSSerhiy Boiko 	if (err)
3398b474a9fSSerhiy Boiko 		goto err_rule_add;
3408b474a9fSSerhiy Boiko 
34147327e19SVolodymyr Mytnyk 	prestera_acl_ruleset_put(ruleset);
3428b474a9fSSerhiy Boiko 	return 0;
3438b474a9fSSerhiy Boiko 
34447327e19SVolodymyr Mytnyk err_ruleset_offload:
3458b474a9fSSerhiy Boiko err_rule_add:
3468b474a9fSSerhiy Boiko 	prestera_acl_rule_destroy(rule);
34747327e19SVolodymyr Mytnyk err_rule_create:
34847327e19SVolodymyr Mytnyk 	prestera_acl_ruleset_put(ruleset);
3498b474a9fSSerhiy Boiko 	return err;
3508b474a9fSSerhiy Boiko }
3518b474a9fSSerhiy Boiko 
3528b474a9fSSerhiy Boiko void prestera_flower_destroy(struct prestera_flow_block *block,
3538b474a9fSSerhiy Boiko 			     struct flow_cls_offload *f)
3548b474a9fSSerhiy Boiko {
35547327e19SVolodymyr Mytnyk 	struct prestera_acl_ruleset *ruleset;
3568b474a9fSSerhiy Boiko 	struct prestera_acl_rule *rule;
3578b474a9fSSerhiy Boiko 
358*fa5d824cSVolodymyr Mytnyk 	ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block,
359*fa5d824cSVolodymyr Mytnyk 					      f->common.chain_index);
36047327e19SVolodymyr Mytnyk 	if (IS_ERR(ruleset))
36147327e19SVolodymyr Mytnyk 		return;
36247327e19SVolodymyr Mytnyk 
36347327e19SVolodymyr Mytnyk 	rule = prestera_acl_rule_lookup(ruleset, f->cookie);
3648b474a9fSSerhiy Boiko 	if (rule) {
36547327e19SVolodymyr Mytnyk 		prestera_acl_rule_del(block->sw, rule);
3668b474a9fSSerhiy Boiko 		prestera_acl_rule_destroy(rule);
3678b474a9fSSerhiy Boiko 	}
36847327e19SVolodymyr Mytnyk 	prestera_acl_ruleset_put(ruleset);
36947327e19SVolodymyr Mytnyk 
3708b474a9fSSerhiy Boiko }
3718b474a9fSSerhiy Boiko 
372604ba230SVolodymyr Mytnyk int prestera_flower_tmplt_create(struct prestera_flow_block *block,
373604ba230SVolodymyr Mytnyk 				 struct flow_cls_offload *f)
374604ba230SVolodymyr Mytnyk {
375604ba230SVolodymyr Mytnyk 	struct prestera_flower_template *template;
376604ba230SVolodymyr Mytnyk 	struct prestera_acl_ruleset *ruleset;
377604ba230SVolodymyr Mytnyk 	struct prestera_acl_rule rule;
378604ba230SVolodymyr Mytnyk 	int err;
379604ba230SVolodymyr Mytnyk 
380604ba230SVolodymyr Mytnyk 	memset(&rule, 0, sizeof(rule));
381604ba230SVolodymyr Mytnyk 	err = prestera_flower_parse(block, &rule, f);
382604ba230SVolodymyr Mytnyk 	if (err)
383604ba230SVolodymyr Mytnyk 		return err;
384604ba230SVolodymyr Mytnyk 
385604ba230SVolodymyr Mytnyk 	template = kmalloc(sizeof(*template), GFP_KERNEL);
386604ba230SVolodymyr Mytnyk 	if (!template) {
387604ba230SVolodymyr Mytnyk 		err = -ENOMEM;
388604ba230SVolodymyr Mytnyk 		goto err_malloc;
389604ba230SVolodymyr Mytnyk 	}
390604ba230SVolodymyr Mytnyk 
391604ba230SVolodymyr Mytnyk 	prestera_acl_rule_keymask_pcl_id_set(&rule, 0);
392*fa5d824cSVolodymyr Mytnyk 	ruleset = prestera_acl_ruleset_get(block->sw->acl, block,
393*fa5d824cSVolodymyr Mytnyk 					   f->common.chain_index);
394604ba230SVolodymyr Mytnyk 	if (IS_ERR_OR_NULL(ruleset)) {
395604ba230SVolodymyr Mytnyk 		err = -EINVAL;
396604ba230SVolodymyr Mytnyk 		goto err_ruleset_get;
397604ba230SVolodymyr Mytnyk 	}
398604ba230SVolodymyr Mytnyk 
399604ba230SVolodymyr Mytnyk 	/* preserve keymask/template to this ruleset */
400604ba230SVolodymyr Mytnyk 	prestera_acl_ruleset_keymask_set(ruleset, rule.re_key.match.mask);
401604ba230SVolodymyr Mytnyk 
402604ba230SVolodymyr Mytnyk 	/* skip error, as it is not possible to reject template operation,
403604ba230SVolodymyr Mytnyk 	 * so, keep the reference to the ruleset for rules to be added
404604ba230SVolodymyr Mytnyk 	 * to that ruleset later. In case of offload fail, the ruleset
405604ba230SVolodymyr Mytnyk 	 * will be offloaded again during adding a new rule. Also,
406604ba230SVolodymyr Mytnyk 	 * unlikly possble that ruleset is already offloaded at this staage.
407604ba230SVolodymyr Mytnyk 	 */
408604ba230SVolodymyr Mytnyk 	prestera_acl_ruleset_offload(ruleset);
409604ba230SVolodymyr Mytnyk 
410604ba230SVolodymyr Mytnyk 	/* keep the reference to the ruleset */
411604ba230SVolodymyr Mytnyk 	template->ruleset = ruleset;
412*fa5d824cSVolodymyr Mytnyk 	template->chain_index = f->common.chain_index;
413*fa5d824cSVolodymyr Mytnyk 	list_add_rcu(&template->list, &block->template_list);
414604ba230SVolodymyr Mytnyk 	return 0;
415604ba230SVolodymyr Mytnyk 
416604ba230SVolodymyr Mytnyk err_ruleset_get:
417604ba230SVolodymyr Mytnyk 	kfree(template);
418604ba230SVolodymyr Mytnyk err_malloc:
419604ba230SVolodymyr Mytnyk 	NL_SET_ERR_MSG_MOD(f->common.extack, "Create chain template failed");
420604ba230SVolodymyr Mytnyk 	return err;
421604ba230SVolodymyr Mytnyk }
422604ba230SVolodymyr Mytnyk 
423604ba230SVolodymyr Mytnyk void prestera_flower_tmplt_destroy(struct prestera_flow_block *block,
424604ba230SVolodymyr Mytnyk 				   struct flow_cls_offload *f)
425604ba230SVolodymyr Mytnyk {
426604ba230SVolodymyr Mytnyk 	prestera_flower_template_cleanup(block);
427604ba230SVolodymyr Mytnyk }
428604ba230SVolodymyr Mytnyk 
4298b474a9fSSerhiy Boiko int prestera_flower_stats(struct prestera_flow_block *block,
4308b474a9fSSerhiy Boiko 			  struct flow_cls_offload *f)
4318b474a9fSSerhiy Boiko {
43247327e19SVolodymyr Mytnyk 	struct prestera_acl_ruleset *ruleset;
4338b474a9fSSerhiy Boiko 	struct prestera_acl_rule *rule;
4348b474a9fSSerhiy Boiko 	u64 packets;
4358b474a9fSSerhiy Boiko 	u64 lastuse;
4368b474a9fSSerhiy Boiko 	u64 bytes;
4378b474a9fSSerhiy Boiko 	int err;
4388b474a9fSSerhiy Boiko 
439*fa5d824cSVolodymyr Mytnyk 	ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block,
440*fa5d824cSVolodymyr Mytnyk 					      f->common.chain_index);
44147327e19SVolodymyr Mytnyk 	if (IS_ERR(ruleset))
44247327e19SVolodymyr Mytnyk 		return PTR_ERR(ruleset);
4438b474a9fSSerhiy Boiko 
44447327e19SVolodymyr Mytnyk 	rule = prestera_acl_rule_lookup(ruleset, f->cookie);
44547327e19SVolodymyr Mytnyk 	if (!rule) {
44647327e19SVolodymyr Mytnyk 		err = -EINVAL;
44747327e19SVolodymyr Mytnyk 		goto err_rule_get_stats;
44847327e19SVolodymyr Mytnyk 	}
44947327e19SVolodymyr Mytnyk 
45047327e19SVolodymyr Mytnyk 	err = prestera_acl_rule_get_stats(block->sw->acl, rule, &packets,
45147327e19SVolodymyr Mytnyk 					  &bytes, &lastuse);
4528b474a9fSSerhiy Boiko 	if (err)
45347327e19SVolodymyr Mytnyk 		goto err_rule_get_stats;
4548b474a9fSSerhiy Boiko 
4558b474a9fSSerhiy Boiko 	flow_stats_update(&f->stats, bytes, packets, 0, lastuse,
45647327e19SVolodymyr Mytnyk 			  FLOW_ACTION_HW_STATS_DELAYED);
45747327e19SVolodymyr Mytnyk 
45847327e19SVolodymyr Mytnyk err_rule_get_stats:
45947327e19SVolodymyr Mytnyk 	prestera_acl_ruleset_put(ruleset);
46047327e19SVolodymyr Mytnyk 	return err;
4618b474a9fSSerhiy Boiko }
462