1c9da1ac1SSteen Hegelund // SPDX-License-Identifier: GPL-2.0+ 2c9da1ac1SSteen Hegelund /* Microchip VCAP API 3c9da1ac1SSteen Hegelund * 4c9da1ac1SSteen Hegelund * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. 5c9da1ac1SSteen Hegelund */ 6c9da1ac1SSteen Hegelund 7c9da1ac1SSteen Hegelund #include <net/tcp.h> 8c9da1ac1SSteen Hegelund 9c9da1ac1SSteen Hegelund #include "sparx5_tc.h" 10c9da1ac1SSteen Hegelund #include "vcap_api.h" 11c9da1ac1SSteen Hegelund #include "vcap_api_client.h" 12c9da1ac1SSteen Hegelund #include "sparx5_main.h" 13c9da1ac1SSteen Hegelund #include "sparx5_vcap_impl.h" 14c9da1ac1SSteen Hegelund 150ca60948SSteen Hegelund #define SPX5_MAX_RULE_SIZE 13 /* allows X1, X2, X4, X6 and X12 rules */ 160ca60948SSteen Hegelund 170ca60948SSteen Hegelund /* Collect keysets and type ids for multiple rules per size */ 180ca60948SSteen Hegelund struct sparx5_wildcard_rule { 190ca60948SSteen Hegelund bool selected; 200ca60948SSteen Hegelund u8 value; 210ca60948SSteen Hegelund u8 mask; 220ca60948SSteen Hegelund enum vcap_keyfield_set keyset; 230ca60948SSteen Hegelund }; 240ca60948SSteen Hegelund 250ca60948SSteen Hegelund struct sparx5_multiple_rules { 260ca60948SSteen Hegelund struct sparx5_wildcard_rule rule[SPX5_MAX_RULE_SIZE]; 270ca60948SSteen Hegelund }; 280ca60948SSteen Hegelund 29c9da1ac1SSteen Hegelund struct sparx5_tc_flower_parse_usage { 30c9da1ac1SSteen Hegelund struct flow_cls_offload *fco; 31c9da1ac1SSteen Hegelund struct flow_rule *frule; 32c9da1ac1SSteen Hegelund struct vcap_rule *vrule; 33d6c2964dSSteen Hegelund u16 l3_proto; 34d6c2964dSSteen Hegelund u8 l4_proto; 35c9da1ac1SSteen Hegelund unsigned int used_keys; 36c9da1ac1SSteen Hegelund }; 37c9da1ac1SSteen Hegelund 38d6c2964dSSteen Hegelund /* These protocols have dedicated keysets in IS2 and a TC dissector 39d6c2964dSSteen Hegelund * ETH_P_ARP does not have a TC dissector 40d6c2964dSSteen Hegelund */ 41d6c2964dSSteen Hegelund static u16 sparx5_tc_known_etypes[] = { 42d6c2964dSSteen Hegelund ETH_P_ALL, 433a344f99SSteen Hegelund ETH_P_ARP, 44d6c2964dSSteen Hegelund ETH_P_IP, 45d6c2964dSSteen Hegelund ETH_P_IPV6, 46d6c2964dSSteen Hegelund }; 47d6c2964dSSteen Hegelund 483a344f99SSteen Hegelund enum sparx5_is2_arp_opcode { 493a344f99SSteen Hegelund SPX5_IS2_ARP_REQUEST, 503a344f99SSteen Hegelund SPX5_IS2_ARP_REPLY, 513a344f99SSteen Hegelund SPX5_IS2_RARP_REQUEST, 523a344f99SSteen Hegelund SPX5_IS2_RARP_REPLY, 533a344f99SSteen Hegelund }; 543a344f99SSteen Hegelund 553a344f99SSteen Hegelund enum tc_arp_opcode { 563a344f99SSteen Hegelund TC_ARP_OP_RESERVED, 573a344f99SSteen Hegelund TC_ARP_OP_REQUEST, 583a344f99SSteen Hegelund TC_ARP_OP_REPLY, 593a344f99SSteen Hegelund }; 603a344f99SSteen Hegelund 61d6c2964dSSteen Hegelund static bool sparx5_tc_is_known_etype(u16 etype) 62d6c2964dSSteen Hegelund { 63d6c2964dSSteen Hegelund int idx; 64d6c2964dSSteen Hegelund 65d6c2964dSSteen Hegelund /* For now this only knows about IS2 traffic classification */ 66d6c2964dSSteen Hegelund for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_known_etypes); ++idx) 67d6c2964dSSteen Hegelund if (sparx5_tc_known_etypes[idx] == etype) 68d6c2964dSSteen Hegelund return true; 69d6c2964dSSteen Hegelund 70d6c2964dSSteen Hegelund return false; 71d6c2964dSSteen Hegelund } 72d6c2964dSSteen Hegelund 73c9da1ac1SSteen Hegelund static int sparx5_tc_flower_handler_ethaddr_usage(struct sparx5_tc_flower_parse_usage *st) 74c9da1ac1SSteen Hegelund { 75c9da1ac1SSteen Hegelund enum vcap_key_field smac_key = VCAP_KF_L2_SMAC; 76c9da1ac1SSteen Hegelund enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC; 77c9da1ac1SSteen Hegelund struct flow_match_eth_addrs match; 78c9da1ac1SSteen Hegelund struct vcap_u48_key smac, dmac; 79c9da1ac1SSteen Hegelund int err = 0; 80c9da1ac1SSteen Hegelund 81c9da1ac1SSteen Hegelund flow_rule_match_eth_addrs(st->frule, &match); 82c9da1ac1SSteen Hegelund 83c9da1ac1SSteen Hegelund if (!is_zero_ether_addr(match.mask->src)) { 84c9da1ac1SSteen Hegelund vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN); 85c9da1ac1SSteen Hegelund vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN); 86c9da1ac1SSteen Hegelund err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac); 87c9da1ac1SSteen Hegelund if (err) 88c9da1ac1SSteen Hegelund goto out; 89c9da1ac1SSteen Hegelund } 90c9da1ac1SSteen Hegelund 91c9da1ac1SSteen Hegelund if (!is_zero_ether_addr(match.mask->dst)) { 92c9da1ac1SSteen Hegelund vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN); 93c9da1ac1SSteen Hegelund vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN); 94c9da1ac1SSteen Hegelund err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac); 95c9da1ac1SSteen Hegelund if (err) 96c9da1ac1SSteen Hegelund goto out; 97c9da1ac1SSteen Hegelund } 98c9da1ac1SSteen Hegelund 99c9da1ac1SSteen Hegelund st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS); 100c9da1ac1SSteen Hegelund 101c9da1ac1SSteen Hegelund return err; 102c9da1ac1SSteen Hegelund 103c9da1ac1SSteen Hegelund out: 104c9da1ac1SSteen Hegelund NL_SET_ERR_MSG_MOD(st->fco->common.extack, "eth_addr parse error"); 105c9da1ac1SSteen Hegelund return err; 106c9da1ac1SSteen Hegelund } 107c9da1ac1SSteen Hegelund 108d6c2964dSSteen Hegelund static int 109d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ipv4_usage(struct sparx5_tc_flower_parse_usage *st) 110d6c2964dSSteen Hegelund { 111d6c2964dSSteen Hegelund int err = 0; 112d6c2964dSSteen Hegelund 113d6c2964dSSteen Hegelund if (st->l3_proto == ETH_P_IP) { 114d6c2964dSSteen Hegelund struct flow_match_ipv4_addrs mt; 115d6c2964dSSteen Hegelund 116d6c2964dSSteen Hegelund flow_rule_match_ipv4_addrs(st->frule, &mt); 117d6c2964dSSteen Hegelund if (mt.mask->src) { 118d6c2964dSSteen Hegelund err = vcap_rule_add_key_u32(st->vrule, 119d6c2964dSSteen Hegelund VCAP_KF_L3_IP4_SIP, 120d6c2964dSSteen Hegelund be32_to_cpu(mt.key->src), 121d6c2964dSSteen Hegelund be32_to_cpu(mt.mask->src)); 122d6c2964dSSteen Hegelund if (err) 123d6c2964dSSteen Hegelund goto out; 124d6c2964dSSteen Hegelund } 125d6c2964dSSteen Hegelund if (mt.mask->dst) { 126d6c2964dSSteen Hegelund err = vcap_rule_add_key_u32(st->vrule, 127d6c2964dSSteen Hegelund VCAP_KF_L3_IP4_DIP, 128d6c2964dSSteen Hegelund be32_to_cpu(mt.key->dst), 129d6c2964dSSteen Hegelund be32_to_cpu(mt.mask->dst)); 130d6c2964dSSteen Hegelund if (err) 131d6c2964dSSteen Hegelund goto out; 132d6c2964dSSteen Hegelund } 133d6c2964dSSteen Hegelund } 134d6c2964dSSteen Hegelund 135d6c2964dSSteen Hegelund st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS); 136d6c2964dSSteen Hegelund 137d6c2964dSSteen Hegelund return err; 138d6c2964dSSteen Hegelund 139d6c2964dSSteen Hegelund out: 140d6c2964dSSteen Hegelund NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv4_addr parse error"); 141d6c2964dSSteen Hegelund return err; 142d6c2964dSSteen Hegelund } 143d6c2964dSSteen Hegelund 144d6c2964dSSteen Hegelund static int 145d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ipv6_usage(struct sparx5_tc_flower_parse_usage *st) 146d6c2964dSSteen Hegelund { 147d6c2964dSSteen Hegelund int err = 0; 148d6c2964dSSteen Hegelund 149d6c2964dSSteen Hegelund if (st->l3_proto == ETH_P_IPV6) { 150d6c2964dSSteen Hegelund struct flow_match_ipv6_addrs mt; 151d6c2964dSSteen Hegelund struct vcap_u128_key sip; 152d6c2964dSSteen Hegelund struct vcap_u128_key dip; 153d6c2964dSSteen Hegelund 154d6c2964dSSteen Hegelund flow_rule_match_ipv6_addrs(st->frule, &mt); 155d6c2964dSSteen Hegelund /* Check if address masks are non-zero */ 156d6c2964dSSteen Hegelund if (!ipv6_addr_any(&mt.mask->src)) { 157d6c2964dSSteen Hegelund vcap_netbytes_copy(sip.value, mt.key->src.s6_addr, 16); 158d6c2964dSSteen Hegelund vcap_netbytes_copy(sip.mask, mt.mask->src.s6_addr, 16); 159d6c2964dSSteen Hegelund err = vcap_rule_add_key_u128(st->vrule, 160d6c2964dSSteen Hegelund VCAP_KF_L3_IP6_SIP, &sip); 161d6c2964dSSteen Hegelund if (err) 162d6c2964dSSteen Hegelund goto out; 163d6c2964dSSteen Hegelund } 164d6c2964dSSteen Hegelund if (!ipv6_addr_any(&mt.mask->dst)) { 165d6c2964dSSteen Hegelund vcap_netbytes_copy(dip.value, mt.key->dst.s6_addr, 16); 166d6c2964dSSteen Hegelund vcap_netbytes_copy(dip.mask, mt.mask->dst.s6_addr, 16); 167d6c2964dSSteen Hegelund err = vcap_rule_add_key_u128(st->vrule, 168d6c2964dSSteen Hegelund VCAP_KF_L3_IP6_DIP, &dip); 169d6c2964dSSteen Hegelund if (err) 170d6c2964dSSteen Hegelund goto out; 171d6c2964dSSteen Hegelund } 172d6c2964dSSteen Hegelund } 173d6c2964dSSteen Hegelund st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS); 174d6c2964dSSteen Hegelund return err; 175d6c2964dSSteen Hegelund out: 176d6c2964dSSteen Hegelund NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv6_addr parse error"); 177d6c2964dSSteen Hegelund return err; 178d6c2964dSSteen Hegelund } 179d6c2964dSSteen Hegelund 180d6c2964dSSteen Hegelund static int 181d6c2964dSSteen Hegelund sparx5_tc_flower_handler_control_usage(struct sparx5_tc_flower_parse_usage *st) 182d6c2964dSSteen Hegelund { 183d6c2964dSSteen Hegelund struct flow_match_control mt; 184d6c2964dSSteen Hegelund u32 value, mask; 185d6c2964dSSteen Hegelund int err = 0; 186d6c2964dSSteen Hegelund 187d6c2964dSSteen Hegelund flow_rule_match_control(st->frule, &mt); 188d6c2964dSSteen Hegelund 189d6c2964dSSteen Hegelund if (mt.mask->flags) { 190d6c2964dSSteen Hegelund if (mt.mask->flags & FLOW_DIS_FIRST_FRAG) { 191d6c2964dSSteen Hegelund if (mt.key->flags & FLOW_DIS_FIRST_FRAG) { 192d6c2964dSSteen Hegelund value = 1; /* initial fragment */ 193d6c2964dSSteen Hegelund mask = 0x3; 194d6c2964dSSteen Hegelund } else { 195d6c2964dSSteen Hegelund if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) { 196d6c2964dSSteen Hegelund value = 3; /* follow up fragment */ 197d6c2964dSSteen Hegelund mask = 0x3; 198d6c2964dSSteen Hegelund } else { 199d6c2964dSSteen Hegelund value = 0; /* no fragment */ 200d6c2964dSSteen Hegelund mask = 0x3; 201d6c2964dSSteen Hegelund } 202d6c2964dSSteen Hegelund } 203d6c2964dSSteen Hegelund } else { 204d6c2964dSSteen Hegelund if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) { 205d6c2964dSSteen Hegelund value = 3; /* follow up fragment */ 206d6c2964dSSteen Hegelund mask = 0x3; 207d6c2964dSSteen Hegelund } else { 208d6c2964dSSteen Hegelund value = 0; /* no fragment */ 209d6c2964dSSteen Hegelund mask = 0x3; 210d6c2964dSSteen Hegelund } 211d6c2964dSSteen Hegelund } 212d6c2964dSSteen Hegelund 213d6c2964dSSteen Hegelund err = vcap_rule_add_key_u32(st->vrule, 214d6c2964dSSteen Hegelund VCAP_KF_L3_FRAGMENT_TYPE, 215d6c2964dSSteen Hegelund value, mask); 216d6c2964dSSteen Hegelund if (err) 217d6c2964dSSteen Hegelund goto out; 218d6c2964dSSteen Hegelund } 219d6c2964dSSteen Hegelund 220d6c2964dSSteen Hegelund st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL); 221d6c2964dSSteen Hegelund 222d6c2964dSSteen Hegelund return err; 223d6c2964dSSteen Hegelund 224d6c2964dSSteen Hegelund out: 225d6c2964dSSteen Hegelund NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error"); 226d6c2964dSSteen Hegelund return err; 227d6c2964dSSteen Hegelund } 228d6c2964dSSteen Hegelund 229d6c2964dSSteen Hegelund static int 230d6c2964dSSteen Hegelund sparx5_tc_flower_handler_portnum_usage(struct sparx5_tc_flower_parse_usage *st) 231d6c2964dSSteen Hegelund { 232d6c2964dSSteen Hegelund struct flow_match_ports mt; 233d6c2964dSSteen Hegelund u16 value, mask; 234d6c2964dSSteen Hegelund int err = 0; 235d6c2964dSSteen Hegelund 236d6c2964dSSteen Hegelund flow_rule_match_ports(st->frule, &mt); 237d6c2964dSSteen Hegelund 238d6c2964dSSteen Hegelund if (mt.mask->src) { 239d6c2964dSSteen Hegelund value = be16_to_cpu(mt.key->src); 240d6c2964dSSteen Hegelund mask = be16_to_cpu(mt.mask->src); 241d6c2964dSSteen Hegelund err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_SPORT, value, 242d6c2964dSSteen Hegelund mask); 243d6c2964dSSteen Hegelund if (err) 244d6c2964dSSteen Hegelund goto out; 245d6c2964dSSteen Hegelund } 246d6c2964dSSteen Hegelund 247d6c2964dSSteen Hegelund if (mt.mask->dst) { 248d6c2964dSSteen Hegelund value = be16_to_cpu(mt.key->dst); 249d6c2964dSSteen Hegelund mask = be16_to_cpu(mt.mask->dst); 250d6c2964dSSteen Hegelund err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_DPORT, value, 251d6c2964dSSteen Hegelund mask); 252d6c2964dSSteen Hegelund if (err) 253d6c2964dSSteen Hegelund goto out; 254d6c2964dSSteen Hegelund } 255d6c2964dSSteen Hegelund 256d6c2964dSSteen Hegelund st->used_keys |= BIT(FLOW_DISSECTOR_KEY_PORTS); 257d6c2964dSSteen Hegelund 258d6c2964dSSteen Hegelund return err; 259d6c2964dSSteen Hegelund 260d6c2964dSSteen Hegelund out: 261d6c2964dSSteen Hegelund NL_SET_ERR_MSG_MOD(st->fco->common.extack, "port parse error"); 262d6c2964dSSteen Hegelund return err; 263d6c2964dSSteen Hegelund } 264d6c2964dSSteen Hegelund 265d6c2964dSSteen Hegelund static int 266d6c2964dSSteen Hegelund sparx5_tc_flower_handler_basic_usage(struct sparx5_tc_flower_parse_usage *st) 267d6c2964dSSteen Hegelund { 268d6c2964dSSteen Hegelund struct flow_match_basic mt; 269d6c2964dSSteen Hegelund int err = 0; 270d6c2964dSSteen Hegelund 271d6c2964dSSteen Hegelund flow_rule_match_basic(st->frule, &mt); 272d6c2964dSSteen Hegelund 273d6c2964dSSteen Hegelund if (mt.mask->n_proto) { 274d6c2964dSSteen Hegelund st->l3_proto = be16_to_cpu(mt.key->n_proto); 275d6c2964dSSteen Hegelund if (!sparx5_tc_is_known_etype(st->l3_proto)) { 276d6c2964dSSteen Hegelund err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE, 277d6c2964dSSteen Hegelund st->l3_proto, ~0); 278d6c2964dSSteen Hegelund if (err) 279d6c2964dSSteen Hegelund goto out; 280d6c2964dSSteen Hegelund } else if (st->l3_proto == ETH_P_IP) { 281d6c2964dSSteen Hegelund err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS, 282d6c2964dSSteen Hegelund VCAP_BIT_1); 283d6c2964dSSteen Hegelund if (err) 284d6c2964dSSteen Hegelund goto out; 285d6c2964dSSteen Hegelund } else if (st->l3_proto == ETH_P_IPV6) { 286d6c2964dSSteen Hegelund err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS, 287d6c2964dSSteen Hegelund VCAP_BIT_0); 288d6c2964dSSteen Hegelund if (err) 289d6c2964dSSteen Hegelund goto out; 290d6c2964dSSteen Hegelund } 291d6c2964dSSteen Hegelund } 292d6c2964dSSteen Hegelund 293d6c2964dSSteen Hegelund if (mt.mask->ip_proto) { 294d6c2964dSSteen Hegelund st->l4_proto = mt.key->ip_proto; 295d6c2964dSSteen Hegelund if (st->l4_proto == IPPROTO_TCP) { 296d6c2964dSSteen Hegelund err = vcap_rule_add_key_bit(st->vrule, 297d6c2964dSSteen Hegelund VCAP_KF_TCP_IS, 298d6c2964dSSteen Hegelund VCAP_BIT_1); 299d6c2964dSSteen Hegelund if (err) 300d6c2964dSSteen Hegelund goto out; 301d6c2964dSSteen Hegelund } else if (st->l4_proto == IPPROTO_UDP) { 302d6c2964dSSteen Hegelund err = vcap_rule_add_key_bit(st->vrule, 303d6c2964dSSteen Hegelund VCAP_KF_TCP_IS, 304d6c2964dSSteen Hegelund VCAP_BIT_0); 305d6c2964dSSteen Hegelund if (err) 306d6c2964dSSteen Hegelund goto out; 307d6c2964dSSteen Hegelund } else { 308d6c2964dSSteen Hegelund err = vcap_rule_add_key_u32(st->vrule, 309d6c2964dSSteen Hegelund VCAP_KF_L3_IP_PROTO, 310d6c2964dSSteen Hegelund st->l4_proto, ~0); 311d6c2964dSSteen Hegelund if (err) 312d6c2964dSSteen Hegelund goto out; 313d6c2964dSSteen Hegelund } 314d6c2964dSSteen Hegelund } 315d6c2964dSSteen Hegelund 316d6c2964dSSteen Hegelund st->used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC); 317d6c2964dSSteen Hegelund 318d6c2964dSSteen Hegelund return err; 319d6c2964dSSteen Hegelund 320d6c2964dSSteen Hegelund out: 321d6c2964dSSteen Hegelund NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error"); 322d6c2964dSSteen Hegelund return err; 323d6c2964dSSteen Hegelund } 324d6c2964dSSteen Hegelund 325d6c2964dSSteen Hegelund static int 326d6c2964dSSteen Hegelund sparx5_tc_flower_handler_vlan_usage(struct sparx5_tc_flower_parse_usage *st) 327d6c2964dSSteen Hegelund { 328d6c2964dSSteen Hegelund enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS; 329d6c2964dSSteen Hegelund enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS; 330d6c2964dSSteen Hegelund struct flow_match_vlan mt; 331d6c2964dSSteen Hegelund int err; 332d6c2964dSSteen Hegelund 333d6c2964dSSteen Hegelund flow_rule_match_vlan(st->frule, &mt); 334d6c2964dSSteen Hegelund 335d6c2964dSSteen Hegelund if (mt.mask->vlan_id) { 336d6c2964dSSteen Hegelund err = vcap_rule_add_key_u32(st->vrule, vid_key, 337d6c2964dSSteen Hegelund mt.key->vlan_id, 338d6c2964dSSteen Hegelund mt.mask->vlan_id); 339d6c2964dSSteen Hegelund if (err) 340d6c2964dSSteen Hegelund goto out; 341d6c2964dSSteen Hegelund } 342d6c2964dSSteen Hegelund 343d6c2964dSSteen Hegelund if (mt.mask->vlan_priority) { 344d6c2964dSSteen Hegelund err = vcap_rule_add_key_u32(st->vrule, pcp_key, 345d6c2964dSSteen Hegelund mt.key->vlan_priority, 346d6c2964dSSteen Hegelund mt.mask->vlan_priority); 347d6c2964dSSteen Hegelund if (err) 348d6c2964dSSteen Hegelund goto out; 349d6c2964dSSteen Hegelund } 350d6c2964dSSteen Hegelund 351d6c2964dSSteen Hegelund st->used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN); 352d6c2964dSSteen Hegelund 3534e9a6139SDan Carpenter return 0; 354d6c2964dSSteen Hegelund out: 355d6c2964dSSteen Hegelund NL_SET_ERR_MSG_MOD(st->fco->common.extack, "vlan parse error"); 356d6c2964dSSteen Hegelund return err; 357d6c2964dSSteen Hegelund } 358d6c2964dSSteen Hegelund 359d6c2964dSSteen Hegelund static int 360d6c2964dSSteen Hegelund sparx5_tc_flower_handler_tcp_usage(struct sparx5_tc_flower_parse_usage *st) 361d6c2964dSSteen Hegelund { 362d6c2964dSSteen Hegelund struct flow_match_tcp mt; 363d6c2964dSSteen Hegelund u16 tcp_flags_mask; 364d6c2964dSSteen Hegelund u16 tcp_flags_key; 365d6c2964dSSteen Hegelund enum vcap_bit val; 366d6c2964dSSteen Hegelund int err = 0; 367d6c2964dSSteen Hegelund 368d6c2964dSSteen Hegelund flow_rule_match_tcp(st->frule, &mt); 369d6c2964dSSteen Hegelund tcp_flags_key = be16_to_cpu(mt.key->flags); 370d6c2964dSSteen Hegelund tcp_flags_mask = be16_to_cpu(mt.mask->flags); 371d6c2964dSSteen Hegelund 372d6c2964dSSteen Hegelund if (tcp_flags_mask & TCPHDR_FIN) { 373d6c2964dSSteen Hegelund val = VCAP_BIT_0; 374d6c2964dSSteen Hegelund if (tcp_flags_key & TCPHDR_FIN) 375d6c2964dSSteen Hegelund val = VCAP_BIT_1; 376d6c2964dSSteen Hegelund err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_FIN, val); 377d6c2964dSSteen Hegelund if (err) 378d6c2964dSSteen Hegelund goto out; 379d6c2964dSSteen Hegelund } 380d6c2964dSSteen Hegelund 381d6c2964dSSteen Hegelund if (tcp_flags_mask & TCPHDR_SYN) { 382d6c2964dSSteen Hegelund val = VCAP_BIT_0; 383d6c2964dSSteen Hegelund if (tcp_flags_key & TCPHDR_SYN) 384d6c2964dSSteen Hegelund val = VCAP_BIT_1; 385d6c2964dSSteen Hegelund err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_SYN, val); 386d6c2964dSSteen Hegelund if (err) 387d6c2964dSSteen Hegelund goto out; 388d6c2964dSSteen Hegelund } 389d6c2964dSSteen Hegelund 390d6c2964dSSteen Hegelund if (tcp_flags_mask & TCPHDR_RST) { 391d6c2964dSSteen Hegelund val = VCAP_BIT_0; 392d6c2964dSSteen Hegelund if (tcp_flags_key & TCPHDR_RST) 393d6c2964dSSteen Hegelund val = VCAP_BIT_1; 394d6c2964dSSteen Hegelund err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_RST, val); 395d6c2964dSSteen Hegelund if (err) 396d6c2964dSSteen Hegelund goto out; 397d6c2964dSSteen Hegelund } 398d6c2964dSSteen Hegelund 399d6c2964dSSteen Hegelund if (tcp_flags_mask & TCPHDR_PSH) { 400d6c2964dSSteen Hegelund val = VCAP_BIT_0; 401d6c2964dSSteen Hegelund if (tcp_flags_key & TCPHDR_PSH) 402d6c2964dSSteen Hegelund val = VCAP_BIT_1; 403d6c2964dSSteen Hegelund err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_PSH, val); 404d6c2964dSSteen Hegelund if (err) 405d6c2964dSSteen Hegelund goto out; 406d6c2964dSSteen Hegelund } 407d6c2964dSSteen Hegelund 408d6c2964dSSteen Hegelund if (tcp_flags_mask & TCPHDR_ACK) { 409d6c2964dSSteen Hegelund val = VCAP_BIT_0; 410d6c2964dSSteen Hegelund if (tcp_flags_key & TCPHDR_ACK) 411d6c2964dSSteen Hegelund val = VCAP_BIT_1; 412d6c2964dSSteen Hegelund err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_ACK, val); 413d6c2964dSSteen Hegelund if (err) 414d6c2964dSSteen Hegelund goto out; 415d6c2964dSSteen Hegelund } 416d6c2964dSSteen Hegelund 417d6c2964dSSteen Hegelund if (tcp_flags_mask & TCPHDR_URG) { 418d6c2964dSSteen Hegelund val = VCAP_BIT_0; 419d6c2964dSSteen Hegelund if (tcp_flags_key & TCPHDR_URG) 420d6c2964dSSteen Hegelund val = VCAP_BIT_1; 421d6c2964dSSteen Hegelund err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_URG, val); 422d6c2964dSSteen Hegelund if (err) 423d6c2964dSSteen Hegelund goto out; 424d6c2964dSSteen Hegelund } 425d6c2964dSSteen Hegelund 426d6c2964dSSteen Hegelund st->used_keys |= BIT(FLOW_DISSECTOR_KEY_TCP); 427d6c2964dSSteen Hegelund 428d6c2964dSSteen Hegelund return err; 429d6c2964dSSteen Hegelund 430d6c2964dSSteen Hegelund out: 431d6c2964dSSteen Hegelund NL_SET_ERR_MSG_MOD(st->fco->common.extack, "tcp_flags parse error"); 432d6c2964dSSteen Hegelund return err; 433d6c2964dSSteen Hegelund } 434d6c2964dSSteen Hegelund 435d6c2964dSSteen Hegelund static int 4363a344f99SSteen Hegelund sparx5_tc_flower_handler_arp_usage(struct sparx5_tc_flower_parse_usage *st) 4373a344f99SSteen Hegelund { 4383a344f99SSteen Hegelund struct flow_match_arp mt; 4393a344f99SSteen Hegelund u16 value, mask; 4403a344f99SSteen Hegelund u32 ipval, ipmsk; 4413a344f99SSteen Hegelund int err; 4423a344f99SSteen Hegelund 4433a344f99SSteen Hegelund flow_rule_match_arp(st->frule, &mt); 4443a344f99SSteen Hegelund 4453a344f99SSteen Hegelund if (mt.mask->op) { 4463a344f99SSteen Hegelund mask = 0x3; 4473a344f99SSteen Hegelund if (st->l3_proto == ETH_P_ARP) { 4483a344f99SSteen Hegelund value = mt.key->op == TC_ARP_OP_REQUEST ? 4493a344f99SSteen Hegelund SPX5_IS2_ARP_REQUEST : 4503a344f99SSteen Hegelund SPX5_IS2_ARP_REPLY; 4513a344f99SSteen Hegelund } else { /* RARP */ 4523a344f99SSteen Hegelund value = mt.key->op == TC_ARP_OP_REQUEST ? 4533a344f99SSteen Hegelund SPX5_IS2_RARP_REQUEST : 4543a344f99SSteen Hegelund SPX5_IS2_RARP_REPLY; 4553a344f99SSteen Hegelund } 4563a344f99SSteen Hegelund err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ARP_OPCODE, 4573a344f99SSteen Hegelund value, mask); 4583a344f99SSteen Hegelund if (err) 4593a344f99SSteen Hegelund goto out; 4603a344f99SSteen Hegelund } 4613a344f99SSteen Hegelund 4623a344f99SSteen Hegelund /* The IS2 ARP keyset does not support ARP hardware addresses */ 4633a344f99SSteen Hegelund if (!is_zero_ether_addr(mt.mask->sha) || 4644e9a6139SDan Carpenter !is_zero_ether_addr(mt.mask->tha)) { 4654e9a6139SDan Carpenter err = -EINVAL; 4663a344f99SSteen Hegelund goto out; 4674e9a6139SDan Carpenter } 4683a344f99SSteen Hegelund 4693a344f99SSteen Hegelund if (mt.mask->sip) { 4703a344f99SSteen Hegelund ipval = be32_to_cpu((__force __be32)mt.key->sip); 4713a344f99SSteen Hegelund ipmsk = be32_to_cpu((__force __be32)mt.mask->sip); 4723a344f99SSteen Hegelund 4733a344f99SSteen Hegelund err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_SIP, 4743a344f99SSteen Hegelund ipval, ipmsk); 4753a344f99SSteen Hegelund if (err) 4763a344f99SSteen Hegelund goto out; 4773a344f99SSteen Hegelund } 4783a344f99SSteen Hegelund 4793a344f99SSteen Hegelund if (mt.mask->tip) { 4803a344f99SSteen Hegelund ipval = be32_to_cpu((__force __be32)mt.key->tip); 4813a344f99SSteen Hegelund ipmsk = be32_to_cpu((__force __be32)mt.mask->tip); 4823a344f99SSteen Hegelund 4833a344f99SSteen Hegelund err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_DIP, 4843a344f99SSteen Hegelund ipval, ipmsk); 4853a344f99SSteen Hegelund if (err) 4863a344f99SSteen Hegelund goto out; 4873a344f99SSteen Hegelund } 4883a344f99SSteen Hegelund 4893a344f99SSteen Hegelund st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ARP); 4903a344f99SSteen Hegelund 4914e9a6139SDan Carpenter return 0; 4923a344f99SSteen Hegelund 4933a344f99SSteen Hegelund out: 4943a344f99SSteen Hegelund NL_SET_ERR_MSG_MOD(st->fco->common.extack, "arp parse error"); 4953a344f99SSteen Hegelund return err; 4963a344f99SSteen Hegelund } 4973a344f99SSteen Hegelund 4983a344f99SSteen Hegelund static int 499d6c2964dSSteen Hegelund sparx5_tc_flower_handler_ip_usage(struct sparx5_tc_flower_parse_usage *st) 500d6c2964dSSteen Hegelund { 501d6c2964dSSteen Hegelund struct flow_match_ip mt; 502d6c2964dSSteen Hegelund int err = 0; 503d6c2964dSSteen Hegelund 504d6c2964dSSteen Hegelund flow_rule_match_ip(st->frule, &mt); 505d6c2964dSSteen Hegelund 506d6c2964dSSteen Hegelund if (mt.mask->tos) { 507d6c2964dSSteen Hegelund err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_TOS, 508d6c2964dSSteen Hegelund mt.key->tos, 509d6c2964dSSteen Hegelund mt.mask->tos); 510d6c2964dSSteen Hegelund if (err) 511d6c2964dSSteen Hegelund goto out; 512d6c2964dSSteen Hegelund } 513d6c2964dSSteen Hegelund 514d6c2964dSSteen Hegelund st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IP); 515d6c2964dSSteen Hegelund 516d6c2964dSSteen Hegelund return err; 517d6c2964dSSteen Hegelund 518d6c2964dSSteen Hegelund out: 519d6c2964dSSteen Hegelund NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_tos parse error"); 520d6c2964dSSteen Hegelund return err; 521d6c2964dSSteen Hegelund } 522d6c2964dSSteen Hegelund 523c9da1ac1SSteen Hegelund static int (*sparx5_tc_flower_usage_handlers[])(struct sparx5_tc_flower_parse_usage *st) = { 524c9da1ac1SSteen Hegelund [FLOW_DISSECTOR_KEY_ETH_ADDRS] = sparx5_tc_flower_handler_ethaddr_usage, 525d6c2964dSSteen Hegelund [FLOW_DISSECTOR_KEY_IPV4_ADDRS] = sparx5_tc_flower_handler_ipv4_usage, 526d6c2964dSSteen Hegelund [FLOW_DISSECTOR_KEY_IPV6_ADDRS] = sparx5_tc_flower_handler_ipv6_usage, 527d6c2964dSSteen Hegelund [FLOW_DISSECTOR_KEY_CONTROL] = sparx5_tc_flower_handler_control_usage, 528d6c2964dSSteen Hegelund [FLOW_DISSECTOR_KEY_PORTS] = sparx5_tc_flower_handler_portnum_usage, 529d6c2964dSSteen Hegelund [FLOW_DISSECTOR_KEY_BASIC] = sparx5_tc_flower_handler_basic_usage, 530d6c2964dSSteen Hegelund [FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage, 531d6c2964dSSteen Hegelund [FLOW_DISSECTOR_KEY_TCP] = sparx5_tc_flower_handler_tcp_usage, 5323a344f99SSteen Hegelund [FLOW_DISSECTOR_KEY_ARP] = sparx5_tc_flower_handler_arp_usage, 533d6c2964dSSteen Hegelund [FLOW_DISSECTOR_KEY_IP] = sparx5_tc_flower_handler_ip_usage, 534c9da1ac1SSteen Hegelund }; 535c9da1ac1SSteen Hegelund 536c9da1ac1SSteen Hegelund static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco, 537c9da1ac1SSteen Hegelund struct vcap_admin *admin, 538abc4010dSSteen Hegelund struct vcap_rule *vrule, 539abc4010dSSteen Hegelund u16 *l3_proto) 540c9da1ac1SSteen Hegelund { 541c9da1ac1SSteen Hegelund struct sparx5_tc_flower_parse_usage state = { 542c9da1ac1SSteen Hegelund .fco = fco, 543c9da1ac1SSteen Hegelund .vrule = vrule, 544abc4010dSSteen Hegelund .l3_proto = ETH_P_ALL, 545c9da1ac1SSteen Hegelund }; 546c9da1ac1SSteen Hegelund int idx, err = 0; 547c9da1ac1SSteen Hegelund 548c9da1ac1SSteen Hegelund state.frule = flow_cls_offload_flow_rule(fco); 549c9da1ac1SSteen Hegelund for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) { 550c9da1ac1SSteen Hegelund if (!flow_rule_match_key(state.frule, idx)) 551c9da1ac1SSteen Hegelund continue; 552c9da1ac1SSteen Hegelund if (!sparx5_tc_flower_usage_handlers[idx]) 553c9da1ac1SSteen Hegelund continue; 554c9da1ac1SSteen Hegelund err = sparx5_tc_flower_usage_handlers[idx](&state); 555c9da1ac1SSteen Hegelund if (err) 556c9da1ac1SSteen Hegelund return err; 557c9da1ac1SSteen Hegelund } 558abc4010dSSteen Hegelund 559abc4010dSSteen Hegelund if (state.frule->match.dissector->used_keys ^ state.used_keys) { 560abc4010dSSteen Hegelund NL_SET_ERR_MSG_MOD(fco->common.extack, 561abc4010dSSteen Hegelund "Unsupported match item"); 562abc4010dSSteen Hegelund return -ENOENT; 563abc4010dSSteen Hegelund } 564abc4010dSSteen Hegelund 565abc4010dSSteen Hegelund if (l3_proto) 566abc4010dSSteen Hegelund *l3_proto = state.l3_proto; 567c9da1ac1SSteen Hegelund return err; 568c9da1ac1SSteen Hegelund } 569c9da1ac1SSteen Hegelund 570392d0ab0SSteen Hegelund static int sparx5_tc_flower_action_check(struct vcap_control *vctrl, 571784c3067SSteen Hegelund struct net_device *ndev, 572784c3067SSteen Hegelund struct flow_cls_offload *fco) 573392d0ab0SSteen Hegelund { 574392d0ab0SSteen Hegelund struct flow_rule *rule = flow_cls_offload_flow_rule(fco); 575392d0ab0SSteen Hegelund struct flow_action_entry *actent, *last_actent = NULL; 576392d0ab0SSteen Hegelund struct flow_action *act = &rule->action; 577392d0ab0SSteen Hegelund u64 action_mask = 0; 578392d0ab0SSteen Hegelund int idx; 579392d0ab0SSteen Hegelund 580392d0ab0SSteen Hegelund if (!flow_action_has_entries(act)) { 581392d0ab0SSteen Hegelund NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions"); 582392d0ab0SSteen Hegelund return -EINVAL; 583392d0ab0SSteen Hegelund } 584392d0ab0SSteen Hegelund 585392d0ab0SSteen Hegelund if (!flow_action_basic_hw_stats_check(act, fco->common.extack)) 586392d0ab0SSteen Hegelund return -EOPNOTSUPP; 587392d0ab0SSteen Hegelund 588392d0ab0SSteen Hegelund flow_action_for_each(idx, actent, act) { 589392d0ab0SSteen Hegelund if (action_mask & BIT(actent->id)) { 590392d0ab0SSteen Hegelund NL_SET_ERR_MSG_MOD(fco->common.extack, 591392d0ab0SSteen Hegelund "More actions of the same type"); 592392d0ab0SSteen Hegelund return -EINVAL; 593392d0ab0SSteen Hegelund } 594392d0ab0SSteen Hegelund action_mask |= BIT(actent->id); 595392d0ab0SSteen Hegelund last_actent = actent; /* Save last action for later check */ 596392d0ab0SSteen Hegelund } 597392d0ab0SSteen Hegelund 598784c3067SSteen Hegelund /* Check if last action is a goto 599784c3067SSteen Hegelund * The last chain/lookup does not need to have a goto action 600784c3067SSteen Hegelund */ 601784c3067SSteen Hegelund if (last_actent->id == FLOW_ACTION_GOTO) { 602784c3067SSteen Hegelund /* Check if the destination chain is in one of the VCAPs */ 603392d0ab0SSteen Hegelund if (!vcap_is_next_lookup(vctrl, fco->common.chain_index, 604392d0ab0SSteen Hegelund last_actent->chain_index)) { 605392d0ab0SSteen Hegelund NL_SET_ERR_MSG_MOD(fco->common.extack, 606392d0ab0SSteen Hegelund "Invalid goto chain"); 607392d0ab0SSteen Hegelund return -EINVAL; 608392d0ab0SSteen Hegelund } 609784c3067SSteen Hegelund } else if (!vcap_is_last_chain(vctrl, fco->common.chain_index)) { 610784c3067SSteen Hegelund NL_SET_ERR_MSG_MOD(fco->common.extack, 611784c3067SSteen Hegelund "Last action must be 'goto'"); 612784c3067SSteen Hegelund return -EINVAL; 613784c3067SSteen Hegelund } 614392d0ab0SSteen Hegelund 615392d0ab0SSteen Hegelund /* Catch unsupported combinations of actions */ 616392d0ab0SSteen Hegelund if (action_mask & BIT(FLOW_ACTION_TRAP) && 617392d0ab0SSteen Hegelund action_mask & BIT(FLOW_ACTION_ACCEPT)) { 618392d0ab0SSteen Hegelund NL_SET_ERR_MSG_MOD(fco->common.extack, 619392d0ab0SSteen Hegelund "Cannot combine pass and trap action"); 620392d0ab0SSteen Hegelund return -EOPNOTSUPP; 621392d0ab0SSteen Hegelund } 622392d0ab0SSteen Hegelund 623392d0ab0SSteen Hegelund return 0; 624392d0ab0SSteen Hegelund } 625392d0ab0SSteen Hegelund 62640e7fe18SSteen Hegelund /* Add a rule counter action - only IS2 is considered for now */ 62740e7fe18SSteen Hegelund static int sparx5_tc_add_rule_counter(struct vcap_admin *admin, 62840e7fe18SSteen Hegelund struct vcap_rule *vrule) 62940e7fe18SSteen Hegelund { 63040e7fe18SSteen Hegelund int err; 63140e7fe18SSteen Hegelund 6320ca60948SSteen Hegelund err = vcap_rule_mod_action_u32(vrule, VCAP_AF_CNT_ID, vrule->id); 63340e7fe18SSteen Hegelund if (err) 63440e7fe18SSteen Hegelund return err; 63540e7fe18SSteen Hegelund 63640e7fe18SSteen Hegelund vcap_rule_set_counter_id(vrule, vrule->id); 63740e7fe18SSteen Hegelund return err; 63840e7fe18SSteen Hegelund } 63940e7fe18SSteen Hegelund 6400ca60948SSteen Hegelund /* Collect all port keysets and apply the first of them, possibly wildcarded */ 6410ca60948SSteen Hegelund static int sparx5_tc_select_protocol_keyset(struct net_device *ndev, 6420ca60948SSteen Hegelund struct vcap_rule *vrule, 6430ca60948SSteen Hegelund struct vcap_admin *admin, 6440ca60948SSteen Hegelund u16 l3_proto, 6450ca60948SSteen Hegelund struct sparx5_multiple_rules *multi) 6460ca60948SSteen Hegelund { 6470ca60948SSteen Hegelund struct sparx5_port *port = netdev_priv(ndev); 6480ca60948SSteen Hegelund struct vcap_keyset_list portkeysetlist = {}; 6490ca60948SSteen Hegelund enum vcap_keyfield_set portkeysets[10] = {}; 6500ca60948SSteen Hegelund struct vcap_keyset_list matches = {}; 6510ca60948SSteen Hegelund enum vcap_keyfield_set keysets[10]; 6520ca60948SSteen Hegelund int idx, jdx, err = 0, count = 0; 6530ca60948SSteen Hegelund struct sparx5_wildcard_rule *mru; 6540ca60948SSteen Hegelund const struct vcap_set *kinfo; 6550ca60948SSteen Hegelund struct vcap_control *vctrl; 6560ca60948SSteen Hegelund 6570ca60948SSteen Hegelund vctrl = port->sparx5->vcap_ctrl; 6580ca60948SSteen Hegelund 6590ca60948SSteen Hegelund /* Find the keysets that the rule can use */ 6600ca60948SSteen Hegelund matches.keysets = keysets; 6610ca60948SSteen Hegelund matches.max = ARRAY_SIZE(keysets); 6620ca60948SSteen Hegelund if (vcap_rule_find_keysets(vrule, &matches) == 0) 6630ca60948SSteen Hegelund return -EINVAL; 6640ca60948SSteen Hegelund 6650ca60948SSteen Hegelund /* Find the keysets that the port configuration supports */ 6660ca60948SSteen Hegelund portkeysetlist.max = ARRAY_SIZE(portkeysets); 6670ca60948SSteen Hegelund portkeysetlist.keysets = portkeysets; 6680ca60948SSteen Hegelund err = sparx5_vcap_get_port_keyset(ndev, 6690ca60948SSteen Hegelund admin, vrule->vcap_chain_id, 6700ca60948SSteen Hegelund l3_proto, 6710ca60948SSteen Hegelund &portkeysetlist); 6720ca60948SSteen Hegelund if (err) 6730ca60948SSteen Hegelund return err; 6740ca60948SSteen Hegelund 6750ca60948SSteen Hegelund /* Find the intersection of the two sets of keyset */ 6760ca60948SSteen Hegelund for (idx = 0; idx < portkeysetlist.cnt; ++idx) { 6770ca60948SSteen Hegelund kinfo = vcap_keyfieldset(vctrl, admin->vtype, 6780ca60948SSteen Hegelund portkeysetlist.keysets[idx]); 6790ca60948SSteen Hegelund if (!kinfo) 6800ca60948SSteen Hegelund continue; 6810ca60948SSteen Hegelund 6820ca60948SSteen Hegelund /* Find a port keyset that matches the required keys 6830ca60948SSteen Hegelund * If there are multiple keysets then compose a type id mask 6840ca60948SSteen Hegelund */ 6850ca60948SSteen Hegelund for (jdx = 0; jdx < matches.cnt; ++jdx) { 6860ca60948SSteen Hegelund if (portkeysetlist.keysets[idx] != matches.keysets[jdx]) 6870ca60948SSteen Hegelund continue; 6880ca60948SSteen Hegelund 6890ca60948SSteen Hegelund mru = &multi->rule[kinfo->sw_per_item]; 6900ca60948SSteen Hegelund if (!mru->selected) { 6910ca60948SSteen Hegelund mru->selected = true; 6920ca60948SSteen Hegelund mru->keyset = portkeysetlist.keysets[idx]; 6930ca60948SSteen Hegelund mru->value = kinfo->type_id; 6940ca60948SSteen Hegelund } 6950ca60948SSteen Hegelund mru->value &= kinfo->type_id; 6960ca60948SSteen Hegelund mru->mask |= kinfo->type_id; 6970ca60948SSteen Hegelund ++count; 6980ca60948SSteen Hegelund } 6990ca60948SSteen Hegelund } 7000ca60948SSteen Hegelund if (count == 0) 7010ca60948SSteen Hegelund return -EPROTO; 7020ca60948SSteen Hegelund 7030ca60948SSteen Hegelund if (l3_proto == ETH_P_ALL && count < portkeysetlist.cnt) 7040ca60948SSteen Hegelund return -ENOENT; 7050ca60948SSteen Hegelund 7060ca60948SSteen Hegelund for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) { 7070ca60948SSteen Hegelund mru = &multi->rule[idx]; 7080ca60948SSteen Hegelund if (!mru->selected) 7090ca60948SSteen Hegelund continue; 7100ca60948SSteen Hegelund 7110ca60948SSteen Hegelund /* Align the mask to the combined value */ 7120ca60948SSteen Hegelund mru->mask ^= mru->value; 7130ca60948SSteen Hegelund } 7140ca60948SSteen Hegelund 7150ca60948SSteen Hegelund /* Set the chosen keyset on the rule and set a wildcarded type if there 7160ca60948SSteen Hegelund * are more than one keyset 7170ca60948SSteen Hegelund */ 7180ca60948SSteen Hegelund for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) { 7190ca60948SSteen Hegelund mru = &multi->rule[idx]; 7200ca60948SSteen Hegelund if (!mru->selected) 7210ca60948SSteen Hegelund continue; 7220ca60948SSteen Hegelund 7230ca60948SSteen Hegelund vcap_set_rule_set_keyset(vrule, mru->keyset); 7240ca60948SSteen Hegelund if (count > 1) 7250ca60948SSteen Hegelund /* Some keysets do not have a type field */ 7260ca60948SSteen Hegelund vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE, 7270ca60948SSteen Hegelund mru->value, 7280ca60948SSteen Hegelund ~mru->mask); 7290ca60948SSteen Hegelund mru->selected = false; /* mark as done */ 7300ca60948SSteen Hegelund break; /* Stop here and add more rules later */ 7310ca60948SSteen Hegelund } 7320ca60948SSteen Hegelund return err; 7330ca60948SSteen Hegelund } 7340ca60948SSteen Hegelund 7350ca60948SSteen Hegelund static int sparx5_tc_add_rule_copy(struct vcap_control *vctrl, 7360ca60948SSteen Hegelund struct flow_cls_offload *fco, 7370ca60948SSteen Hegelund struct vcap_rule *erule, 7380ca60948SSteen Hegelund struct vcap_admin *admin, 7390ca60948SSteen Hegelund struct sparx5_wildcard_rule *rule) 7400ca60948SSteen Hegelund { 7410ca60948SSteen Hegelund enum vcap_key_field keylist[] = { 7420ca60948SSteen Hegelund VCAP_KF_IF_IGR_PORT_MASK, 7430ca60948SSteen Hegelund VCAP_KF_IF_IGR_PORT_MASK_SEL, 7440ca60948SSteen Hegelund VCAP_KF_IF_IGR_PORT_MASK_RNG, 7450ca60948SSteen Hegelund VCAP_KF_LOOKUP_FIRST_IS, 7460ca60948SSteen Hegelund VCAP_KF_TYPE, 7470ca60948SSteen Hegelund }; 7480ca60948SSteen Hegelund struct vcap_rule *vrule; 7490ca60948SSteen Hegelund int err; 7500ca60948SSteen Hegelund 7510ca60948SSteen Hegelund /* Add an extra rule with a special user and the new keyset */ 7520ca60948SSteen Hegelund erule->user = VCAP_USER_TC_EXTRA; 7530ca60948SSteen Hegelund vrule = vcap_copy_rule(erule); 7540ca60948SSteen Hegelund if (IS_ERR(vrule)) 7550ca60948SSteen Hegelund return PTR_ERR(vrule); 7560ca60948SSteen Hegelund 7570ca60948SSteen Hegelund /* Link the new rule to the existing rule with the cookie */ 7580ca60948SSteen Hegelund vrule->cookie = erule->cookie; 7590ca60948SSteen Hegelund vcap_filter_rule_keys(vrule, keylist, ARRAY_SIZE(keylist), true); 7600ca60948SSteen Hegelund err = vcap_set_rule_set_keyset(vrule, rule->keyset); 7610ca60948SSteen Hegelund if (err) { 7620ca60948SSteen Hegelund pr_err("%s:%d: could not set keyset %s in rule: %u\n", 7630ca60948SSteen Hegelund __func__, __LINE__, 7640ca60948SSteen Hegelund vcap_keyset_name(vctrl, rule->keyset), 7650ca60948SSteen Hegelund vrule->id); 7660ca60948SSteen Hegelund goto out; 7670ca60948SSteen Hegelund } 7680ca60948SSteen Hegelund 7690ca60948SSteen Hegelund /* Some keysets do not have a type field, so ignore return value */ 7700ca60948SSteen Hegelund vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE, rule->value, ~rule->mask); 7710ca60948SSteen Hegelund 7720ca60948SSteen Hegelund err = vcap_set_rule_set_actionset(vrule, erule->actionset); 7730ca60948SSteen Hegelund if (err) 7740ca60948SSteen Hegelund goto out; 7750ca60948SSteen Hegelund 7760ca60948SSteen Hegelund err = sparx5_tc_add_rule_counter(admin, vrule); 7770ca60948SSteen Hegelund if (err) 7780ca60948SSteen Hegelund goto out; 7790ca60948SSteen Hegelund 7800ca60948SSteen Hegelund err = vcap_val_rule(vrule, ETH_P_ALL); 7810ca60948SSteen Hegelund if (err) { 7820ca60948SSteen Hegelund pr_err("%s:%d: could not validate rule: %u\n", 7830ca60948SSteen Hegelund __func__, __LINE__, vrule->id); 7840ca60948SSteen Hegelund vcap_set_tc_exterr(fco, vrule); 7850ca60948SSteen Hegelund goto out; 7860ca60948SSteen Hegelund } 7870ca60948SSteen Hegelund err = vcap_add_rule(vrule); 7880ca60948SSteen Hegelund if (err) { 7890ca60948SSteen Hegelund pr_err("%s:%d: could not add rule: %u\n", 7900ca60948SSteen Hegelund __func__, __LINE__, vrule->id); 7910ca60948SSteen Hegelund goto out; 7920ca60948SSteen Hegelund } 7930ca60948SSteen Hegelund out: 7940ca60948SSteen Hegelund vcap_free_rule(vrule); 7950ca60948SSteen Hegelund return err; 7960ca60948SSteen Hegelund } 7970ca60948SSteen Hegelund 7980ca60948SSteen Hegelund static int sparx5_tc_add_remaining_rules(struct vcap_control *vctrl, 7990ca60948SSteen Hegelund struct flow_cls_offload *fco, 8000ca60948SSteen Hegelund struct vcap_rule *erule, 8010ca60948SSteen Hegelund struct vcap_admin *admin, 8020ca60948SSteen Hegelund struct sparx5_multiple_rules *multi) 8030ca60948SSteen Hegelund { 8040ca60948SSteen Hegelund int idx, err = 0; 8050ca60948SSteen Hegelund 8060ca60948SSteen Hegelund for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) { 8070ca60948SSteen Hegelund if (!multi->rule[idx].selected) 8080ca60948SSteen Hegelund continue; 8090ca60948SSteen Hegelund 8100ca60948SSteen Hegelund err = sparx5_tc_add_rule_copy(vctrl, fco, erule, admin, 8110ca60948SSteen Hegelund &multi->rule[idx]); 8120ca60948SSteen Hegelund if (err) 8130ca60948SSteen Hegelund break; 8140ca60948SSteen Hegelund } 8150ca60948SSteen Hegelund return err; 8160ca60948SSteen Hegelund } 8170ca60948SSteen Hegelund 818c9da1ac1SSteen Hegelund static int sparx5_tc_flower_replace(struct net_device *ndev, 819c9da1ac1SSteen Hegelund struct flow_cls_offload *fco, 820c9da1ac1SSteen Hegelund struct vcap_admin *admin) 821c9da1ac1SSteen Hegelund { 822c9da1ac1SSteen Hegelund struct sparx5_port *port = netdev_priv(ndev); 8230ca60948SSteen Hegelund struct sparx5_multiple_rules multi = {}; 824c9da1ac1SSteen Hegelund struct flow_action_entry *act; 825c9da1ac1SSteen Hegelund struct vcap_control *vctrl; 826c9da1ac1SSteen Hegelund struct flow_rule *frule; 827c9da1ac1SSteen Hegelund struct vcap_rule *vrule; 828abc4010dSSteen Hegelund u16 l3_proto; 829c9da1ac1SSteen Hegelund int err, idx; 830c9da1ac1SSteen Hegelund 831c9da1ac1SSteen Hegelund vctrl = port->sparx5->vcap_ctrl; 832392d0ab0SSteen Hegelund 833784c3067SSteen Hegelund err = sparx5_tc_flower_action_check(vctrl, ndev, fco); 834392d0ab0SSteen Hegelund if (err) 835392d0ab0SSteen Hegelund return err; 836392d0ab0SSteen Hegelund 837c9da1ac1SSteen Hegelund vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, VCAP_USER_TC, 838c9da1ac1SSteen Hegelund fco->common.prio, 0); 839c9da1ac1SSteen Hegelund if (IS_ERR(vrule)) 840c9da1ac1SSteen Hegelund return PTR_ERR(vrule); 841c9da1ac1SSteen Hegelund 842c9da1ac1SSteen Hegelund vrule->cookie = fco->cookie; 843bcddc196SSteen Hegelund 844bcddc196SSteen Hegelund l3_proto = ETH_P_ALL; 845bcddc196SSteen Hegelund err = sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto); 846bcddc196SSteen Hegelund if (err) 847bcddc196SSteen Hegelund goto out; 84840e7fe18SSteen Hegelund 84940e7fe18SSteen Hegelund err = sparx5_tc_add_rule_counter(admin, vrule); 85040e7fe18SSteen Hegelund if (err) 85140e7fe18SSteen Hegelund goto out; 85240e7fe18SSteen Hegelund 853392d0ab0SSteen Hegelund frule = flow_cls_offload_flow_rule(fco); 854c9da1ac1SSteen Hegelund flow_action_for_each(idx, act, &frule->action) { 855c9da1ac1SSteen Hegelund switch (act->id) { 856c9da1ac1SSteen Hegelund case FLOW_ACTION_TRAP: 857c9da1ac1SSteen Hegelund err = vcap_rule_add_action_bit(vrule, 858c9da1ac1SSteen Hegelund VCAP_AF_CPU_COPY_ENA, 859c9da1ac1SSteen Hegelund VCAP_BIT_1); 860c9da1ac1SSteen Hegelund if (err) 861c9da1ac1SSteen Hegelund goto out; 862c9da1ac1SSteen Hegelund err = vcap_rule_add_action_u32(vrule, 863c9da1ac1SSteen Hegelund VCAP_AF_CPU_QUEUE_NUM, 0); 864c9da1ac1SSteen Hegelund if (err) 865c9da1ac1SSteen Hegelund goto out; 866c9da1ac1SSteen Hegelund err = vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE, 867c9da1ac1SSteen Hegelund SPX5_PMM_REPLACE_ALL); 868c9da1ac1SSteen Hegelund if (err) 869c9da1ac1SSteen Hegelund goto out; 870c9da1ac1SSteen Hegelund /* For now the actionset is hardcoded */ 871c9da1ac1SSteen Hegelund err = vcap_set_rule_set_actionset(vrule, 872c9da1ac1SSteen Hegelund VCAP_AFS_BASE_TYPE); 873c9da1ac1SSteen Hegelund if (err) 874c9da1ac1SSteen Hegelund goto out; 875c9da1ac1SSteen Hegelund break; 876c9da1ac1SSteen Hegelund case FLOW_ACTION_ACCEPT: 877c9da1ac1SSteen Hegelund /* For now the actionset is hardcoded */ 878c9da1ac1SSteen Hegelund err = vcap_set_rule_set_actionset(vrule, 879c9da1ac1SSteen Hegelund VCAP_AFS_BASE_TYPE); 880c9da1ac1SSteen Hegelund if (err) 881c9da1ac1SSteen Hegelund goto out; 882c9da1ac1SSteen Hegelund break; 883392d0ab0SSteen Hegelund case FLOW_ACTION_GOTO: 884392d0ab0SSteen Hegelund /* Links between VCAPs will be added later */ 885392d0ab0SSteen Hegelund break; 886c9da1ac1SSteen Hegelund default: 887c9da1ac1SSteen Hegelund NL_SET_ERR_MSG_MOD(fco->common.extack, 888c9da1ac1SSteen Hegelund "Unsupported TC action"); 889c9da1ac1SSteen Hegelund err = -EOPNOTSUPP; 890c9da1ac1SSteen Hegelund goto out; 891c9da1ac1SSteen Hegelund } 892c9da1ac1SSteen Hegelund } 8930ca60948SSteen Hegelund 8940ca60948SSteen Hegelund err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin, l3_proto, 8950ca60948SSteen Hegelund &multi); 8960ca60948SSteen Hegelund if (err) { 8970ca60948SSteen Hegelund NL_SET_ERR_MSG_MOD(fco->common.extack, 8980ca60948SSteen Hegelund "No matching port keyset for filter protocol and keys"); 8990ca60948SSteen Hegelund goto out; 9000ca60948SSteen Hegelund } 9010ca60948SSteen Hegelund 902abc4010dSSteen Hegelund /* provide the l3 protocol to guide the keyset selection */ 903abc4010dSSteen Hegelund err = vcap_val_rule(vrule, l3_proto); 904c9da1ac1SSteen Hegelund if (err) { 905c9da1ac1SSteen Hegelund vcap_set_tc_exterr(fco, vrule); 906c9da1ac1SSteen Hegelund goto out; 907c9da1ac1SSteen Hegelund } 908c9da1ac1SSteen Hegelund err = vcap_add_rule(vrule); 909c9da1ac1SSteen Hegelund if (err) 910c9da1ac1SSteen Hegelund NL_SET_ERR_MSG_MOD(fco->common.extack, 911c9da1ac1SSteen Hegelund "Could not add the filter"); 9120ca60948SSteen Hegelund 9130ca60948SSteen Hegelund if (l3_proto == ETH_P_ALL) 9140ca60948SSteen Hegelund err = sparx5_tc_add_remaining_rules(vctrl, fco, vrule, admin, 9150ca60948SSteen Hegelund &multi); 9160ca60948SSteen Hegelund 917c9da1ac1SSteen Hegelund out: 918c9da1ac1SSteen Hegelund vcap_free_rule(vrule); 919c9da1ac1SSteen Hegelund return err; 920c9da1ac1SSteen Hegelund } 921c9da1ac1SSteen Hegelund 922c9da1ac1SSteen Hegelund static int sparx5_tc_flower_destroy(struct net_device *ndev, 923c9da1ac1SSteen Hegelund struct flow_cls_offload *fco, 924c9da1ac1SSteen Hegelund struct vcap_admin *admin) 925c9da1ac1SSteen Hegelund { 926c9da1ac1SSteen Hegelund struct sparx5_port *port = netdev_priv(ndev); 927c9da1ac1SSteen Hegelund struct vcap_control *vctrl; 928c9da1ac1SSteen Hegelund int err = -ENOENT, rule_id; 929c9da1ac1SSteen Hegelund 930c9da1ac1SSteen Hegelund vctrl = port->sparx5->vcap_ctrl; 931c9da1ac1SSteen Hegelund while (true) { 932c9da1ac1SSteen Hegelund rule_id = vcap_lookup_rule_by_cookie(vctrl, fco->cookie); 933c9da1ac1SSteen Hegelund if (rule_id <= 0) 934c9da1ac1SSteen Hegelund break; 935c9da1ac1SSteen Hegelund err = vcap_del_rule(vctrl, ndev, rule_id); 936c9da1ac1SSteen Hegelund if (err) { 937c9da1ac1SSteen Hegelund pr_err("%s:%d: could not delete rule %d\n", 938c9da1ac1SSteen Hegelund __func__, __LINE__, rule_id); 939c9da1ac1SSteen Hegelund break; 940c9da1ac1SSteen Hegelund } 941c9da1ac1SSteen Hegelund } 942c9da1ac1SSteen Hegelund return err; 943c9da1ac1SSteen Hegelund } 944c9da1ac1SSteen Hegelund 94540e7fe18SSteen Hegelund static int sparx5_tc_flower_stats(struct net_device *ndev, 94640e7fe18SSteen Hegelund struct flow_cls_offload *fco, 94740e7fe18SSteen Hegelund struct vcap_admin *admin) 94840e7fe18SSteen Hegelund { 94940e7fe18SSteen Hegelund struct sparx5_port *port = netdev_priv(ndev); 950*27d293ccSSteen Hegelund struct vcap_counter ctr = {}; 95140e7fe18SSteen Hegelund struct vcap_control *vctrl; 95240e7fe18SSteen Hegelund ulong lastused = 0; 95340e7fe18SSteen Hegelund int err; 95440e7fe18SSteen Hegelund 95540e7fe18SSteen Hegelund vctrl = port->sparx5->vcap_ctrl; 956*27d293ccSSteen Hegelund err = vcap_get_rule_count_by_cookie(vctrl, &ctr, fco->cookie); 95740e7fe18SSteen Hegelund if (err) 95840e7fe18SSteen Hegelund return err; 959*27d293ccSSteen Hegelund flow_stats_update(&fco->stats, 0x0, ctr.value, 0, lastused, 96040e7fe18SSteen Hegelund FLOW_ACTION_HW_STATS_IMMEDIATE); 96140e7fe18SSteen Hegelund return err; 96240e7fe18SSteen Hegelund } 96340e7fe18SSteen Hegelund 964c9da1ac1SSteen Hegelund int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco, 965c9da1ac1SSteen Hegelund bool ingress) 966c9da1ac1SSteen Hegelund { 967c9da1ac1SSteen Hegelund struct sparx5_port *port = netdev_priv(ndev); 968c9da1ac1SSteen Hegelund struct vcap_control *vctrl; 969c9da1ac1SSteen Hegelund struct vcap_admin *admin; 970c9da1ac1SSteen Hegelund int err = -EINVAL; 971c9da1ac1SSteen Hegelund 972c9da1ac1SSteen Hegelund /* Get vcap instance from the chain id */ 973c9da1ac1SSteen Hegelund vctrl = port->sparx5->vcap_ctrl; 974c9da1ac1SSteen Hegelund admin = vcap_find_admin(vctrl, fco->common.chain_index); 975c9da1ac1SSteen Hegelund if (!admin) { 976c9da1ac1SSteen Hegelund NL_SET_ERR_MSG_MOD(fco->common.extack, "Invalid chain"); 977c9da1ac1SSteen Hegelund return err; 978c9da1ac1SSteen Hegelund } 979c9da1ac1SSteen Hegelund 980c9da1ac1SSteen Hegelund switch (fco->command) { 981c9da1ac1SSteen Hegelund case FLOW_CLS_REPLACE: 982c9da1ac1SSteen Hegelund return sparx5_tc_flower_replace(ndev, fco, admin); 983c9da1ac1SSteen Hegelund case FLOW_CLS_DESTROY: 984c9da1ac1SSteen Hegelund return sparx5_tc_flower_destroy(ndev, fco, admin); 98540e7fe18SSteen Hegelund case FLOW_CLS_STATS: 98640e7fe18SSteen Hegelund return sparx5_tc_flower_stats(ndev, fco, admin); 987c9da1ac1SSteen Hegelund default: 988c9da1ac1SSteen Hegelund return -EOPNOTSUPP; 989c9da1ac1SSteen Hegelund } 990c9da1ac1SSteen Hegelund } 991