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