diff options
Diffstat (limited to 'drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c')
-rw-r--r-- | drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c | 787 |
1 files changed, 333 insertions, 454 deletions
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c index 59d6ed6f4191..d73668dcc6b6 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c @@ -4,11 +4,13 @@ * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. */ +#include <net/tc_act/tc_gate.h> #include <net/tcp.h> #include "sparx5_tc.h" #include "vcap_api.h" #include "vcap_api_client.h" +#include "vcap_tc.h" #include "sparx5_main.h" #include "sparx5_vcap_impl.h" @@ -26,223 +28,8 @@ struct sparx5_multiple_rules { struct sparx5_wildcard_rule rule[SPX5_MAX_RULE_SIZE]; }; -struct sparx5_tc_flower_parse_usage { - struct flow_cls_offload *fco; - struct flow_rule *frule; - struct vcap_rule *vrule; - struct vcap_admin *admin; - u16 l3_proto; - u8 l4_proto; - unsigned int used_keys; -}; - -enum sparx5_is2_arp_opcode { - SPX5_IS2_ARP_REQUEST, - SPX5_IS2_ARP_REPLY, - SPX5_IS2_RARP_REQUEST, - SPX5_IS2_RARP_REPLY, -}; - -enum tc_arp_opcode { - TC_ARP_OP_RESERVED, - TC_ARP_OP_REQUEST, - TC_ARP_OP_REPLY, -}; - -static int sparx5_tc_flower_handler_ethaddr_usage(struct sparx5_tc_flower_parse_usage *st) -{ - enum vcap_key_field smac_key = VCAP_KF_L2_SMAC; - enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC; - struct flow_match_eth_addrs match; - struct vcap_u48_key smac, dmac; - int err = 0; - - flow_rule_match_eth_addrs(st->frule, &match); - - if (!is_zero_ether_addr(match.mask->src)) { - vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN); - vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN); - err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac); - if (err) - goto out; - } - - if (!is_zero_ether_addr(match.mask->dst)) { - vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN); - vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN); - err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac); - if (err) - goto out; - } - - st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS); - - return err; - -out: - NL_SET_ERR_MSG_MOD(st->fco->common.extack, "eth_addr parse error"); - return err; -} - static int -sparx5_tc_flower_handler_ipv4_usage(struct sparx5_tc_flower_parse_usage *st) -{ - int err = 0; - - if (st->l3_proto == ETH_P_IP) { - struct flow_match_ipv4_addrs mt; - - flow_rule_match_ipv4_addrs(st->frule, &mt); - if (mt.mask->src) { - err = vcap_rule_add_key_u32(st->vrule, - VCAP_KF_L3_IP4_SIP, - be32_to_cpu(mt.key->src), - be32_to_cpu(mt.mask->src)); - if (err) - goto out; - } - if (mt.mask->dst) { - err = vcap_rule_add_key_u32(st->vrule, - VCAP_KF_L3_IP4_DIP, - be32_to_cpu(mt.key->dst), - be32_to_cpu(mt.mask->dst)); - if (err) - goto out; - } - } - - st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS); - - return err; - -out: - NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv4_addr parse error"); - return err; -} - -static int -sparx5_tc_flower_handler_ipv6_usage(struct sparx5_tc_flower_parse_usage *st) -{ - int err = 0; - - if (st->l3_proto == ETH_P_IPV6) { - struct flow_match_ipv6_addrs mt; - struct vcap_u128_key sip; - struct vcap_u128_key dip; - - flow_rule_match_ipv6_addrs(st->frule, &mt); - /* Check if address masks are non-zero */ - if (!ipv6_addr_any(&mt.mask->src)) { - vcap_netbytes_copy(sip.value, mt.key->src.s6_addr, 16); - vcap_netbytes_copy(sip.mask, mt.mask->src.s6_addr, 16); - err = vcap_rule_add_key_u128(st->vrule, - VCAP_KF_L3_IP6_SIP, &sip); - if (err) - goto out; - } - if (!ipv6_addr_any(&mt.mask->dst)) { - vcap_netbytes_copy(dip.value, mt.key->dst.s6_addr, 16); - vcap_netbytes_copy(dip.mask, mt.mask->dst.s6_addr, 16); - err = vcap_rule_add_key_u128(st->vrule, - VCAP_KF_L3_IP6_DIP, &dip); - if (err) - goto out; - } - } - st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS); - return err; -out: - NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv6_addr parse error"); - return err; -} - -static int -sparx5_tc_flower_handler_control_usage(struct sparx5_tc_flower_parse_usage *st) -{ - struct flow_match_control mt; - u32 value, mask; - int err = 0; - - flow_rule_match_control(st->frule, &mt); - - if (mt.mask->flags) { - if (mt.mask->flags & FLOW_DIS_FIRST_FRAG) { - if (mt.key->flags & FLOW_DIS_FIRST_FRAG) { - value = 1; /* initial fragment */ - mask = 0x3; - } else { - if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) { - value = 3; /* follow up fragment */ - mask = 0x3; - } else { - value = 0; /* no fragment */ - mask = 0x3; - } - } - } else { - if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) { - value = 3; /* follow up fragment */ - mask = 0x3; - } else { - value = 0; /* no fragment */ - mask = 0x3; - } - } - - err = vcap_rule_add_key_u32(st->vrule, - VCAP_KF_L3_FRAGMENT_TYPE, - value, mask); - if (err) - goto out; - } - - st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL); - - return err; - -out: - NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error"); - return err; -} - -static int -sparx5_tc_flower_handler_portnum_usage(struct sparx5_tc_flower_parse_usage *st) -{ - struct flow_match_ports mt; - u16 value, mask; - int err = 0; - - flow_rule_match_ports(st->frule, &mt); - - if (mt.mask->src) { - value = be16_to_cpu(mt.key->src); - mask = be16_to_cpu(mt.mask->src); - err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_SPORT, value, - mask); - if (err) - goto out; - } - - if (mt.mask->dst) { - value = be16_to_cpu(mt.key->dst); - mask = be16_to_cpu(mt.mask->dst); - err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_DPORT, value, - mask); - if (err) - goto out; - } - - st->used_keys |= BIT(FLOW_DISSECTOR_KEY_PORTS); - - return err; - -out: - NL_SET_ERR_MSG_MOD(st->fco->common.extack, "port parse error"); - return err; -} - -static int -sparx5_tc_flower_handler_basic_usage(struct sparx5_tc_flower_parse_usage *st) +sparx5_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st) { struct flow_match_basic mt; int err = 0; @@ -266,6 +53,13 @@ sparx5_tc_flower_handler_basic_usage(struct sparx5_tc_flower_parse_usage *st) VCAP_BIT_0); if (err) goto out; + if (st->admin->vtype == VCAP_TYPE_IS0) { + err = vcap_rule_add_key_bit(st->vrule, + VCAP_KF_IP_SNAP_IS, + VCAP_BIT_1); + if (err) + goto out; + } } } @@ -309,265 +103,92 @@ out: } static int -sparx5_tc_flower_handler_cvlan_usage(struct sparx5_tc_flower_parse_usage *st) -{ - enum vcap_key_field vid_key = VCAP_KF_8021Q_VID0; - enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP0; - struct flow_match_vlan mt; - u16 tpid; - int err; - - if (st->admin->vtype != VCAP_TYPE_IS0) - return -EINVAL; - - flow_rule_match_cvlan(st->frule, &mt); - - tpid = be16_to_cpu(mt.key->vlan_tpid); - - if (tpid == ETH_P_8021Q) { - vid_key = VCAP_KF_8021Q_VID1; - pcp_key = VCAP_KF_8021Q_PCP1; - } - - if (mt.mask->vlan_id) { - err = vcap_rule_add_key_u32(st->vrule, vid_key, - mt.key->vlan_id, - mt.mask->vlan_id); - if (err) - goto out; - } - - if (mt.mask->vlan_priority) { - err = vcap_rule_add_key_u32(st->vrule, pcp_key, - mt.key->vlan_priority, - mt.mask->vlan_priority); - if (err) - goto out; - } - - st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CVLAN); - - return 0; -out: - NL_SET_ERR_MSG_MOD(st->fco->common.extack, "cvlan parse error"); - return err; -} - -static int -sparx5_tc_flower_handler_vlan_usage(struct sparx5_tc_flower_parse_usage *st) -{ - enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS; - enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS; - struct flow_match_vlan mt; - int err; - - flow_rule_match_vlan(st->frule, &mt); - - if (st->admin->vtype == VCAP_TYPE_IS0) { - vid_key = VCAP_KF_8021Q_VID0; - pcp_key = VCAP_KF_8021Q_PCP0; - } - - if (mt.mask->vlan_id) { - err = vcap_rule_add_key_u32(st->vrule, vid_key, - mt.key->vlan_id, - mt.mask->vlan_id); - if (err) - goto out; - } - - if (mt.mask->vlan_priority) { - err = vcap_rule_add_key_u32(st->vrule, pcp_key, - mt.key->vlan_priority, - mt.mask->vlan_priority); - if (err) - goto out; - } - - st->used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN); - - return 0; -out: - NL_SET_ERR_MSG_MOD(st->fco->common.extack, "vlan parse error"); - return err; -} - -static int -sparx5_tc_flower_handler_tcp_usage(struct sparx5_tc_flower_parse_usage *st) +sparx5_tc_flower_handler_control_usage(struct vcap_tc_flower_parse_usage *st) { - struct flow_match_tcp mt; - u16 tcp_flags_mask; - u16 tcp_flags_key; - enum vcap_bit val; + struct flow_match_control mt; + u32 value, mask; int err = 0; - flow_rule_match_tcp(st->frule, &mt); - tcp_flags_key = be16_to_cpu(mt.key->flags); - tcp_flags_mask = be16_to_cpu(mt.mask->flags); - - if (tcp_flags_mask & TCPHDR_FIN) { - val = VCAP_BIT_0; - if (tcp_flags_key & TCPHDR_FIN) - val = VCAP_BIT_1; - err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_FIN, val); - if (err) - goto out; - } - - if (tcp_flags_mask & TCPHDR_SYN) { - val = VCAP_BIT_0; - if (tcp_flags_key & TCPHDR_SYN) - val = VCAP_BIT_1; - err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_SYN, val); - if (err) - goto out; - } - - if (tcp_flags_mask & TCPHDR_RST) { - val = VCAP_BIT_0; - if (tcp_flags_key & TCPHDR_RST) - val = VCAP_BIT_1; - err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_RST, val); - if (err) - goto out; - } - - if (tcp_flags_mask & TCPHDR_PSH) { - val = VCAP_BIT_0; - if (tcp_flags_key & TCPHDR_PSH) - val = VCAP_BIT_1; - err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_PSH, val); - if (err) - goto out; - } + flow_rule_match_control(st->frule, &mt); - if (tcp_flags_mask & TCPHDR_ACK) { - val = VCAP_BIT_0; - if (tcp_flags_key & TCPHDR_ACK) - val = VCAP_BIT_1; - err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_ACK, val); - if (err) - goto out; - } + if (mt.mask->flags) { + if (mt.mask->flags & FLOW_DIS_FIRST_FRAG) { + if (mt.key->flags & FLOW_DIS_FIRST_FRAG) { + value = 1; /* initial fragment */ + mask = 0x3; + } else { + if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) { + value = 3; /* follow up fragment */ + mask = 0x3; + } else { + value = 0; /* no fragment */ + mask = 0x3; + } + } + } else { + if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) { + value = 3; /* follow up fragment */ + mask = 0x3; + } else { + value = 0; /* no fragment */ + mask = 0x3; + } + } - if (tcp_flags_mask & TCPHDR_URG) { - val = VCAP_BIT_0; - if (tcp_flags_key & TCPHDR_URG) - val = VCAP_BIT_1; - err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_URG, val); + err = vcap_rule_add_key_u32(st->vrule, + VCAP_KF_L3_FRAGMENT_TYPE, + value, mask); if (err) goto out; } - st->used_keys |= BIT(FLOW_DISSECTOR_KEY_TCP); + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL); return err; out: - NL_SET_ERR_MSG_MOD(st->fco->common.extack, "tcp_flags parse error"); + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error"); return err; } static int -sparx5_tc_flower_handler_arp_usage(struct sparx5_tc_flower_parse_usage *st) +sparx5_tc_flower_handler_cvlan_usage(struct vcap_tc_flower_parse_usage *st) { - struct flow_match_arp mt; - u16 value, mask; - u32 ipval, ipmsk; - int err; - - flow_rule_match_arp(st->frule, &mt); - - if (mt.mask->op) { - mask = 0x3; - if (st->l3_proto == ETH_P_ARP) { - value = mt.key->op == TC_ARP_OP_REQUEST ? - SPX5_IS2_ARP_REQUEST : - SPX5_IS2_ARP_REPLY; - } else { /* RARP */ - value = mt.key->op == TC_ARP_OP_REQUEST ? - SPX5_IS2_RARP_REQUEST : - SPX5_IS2_RARP_REPLY; - } - err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ARP_OPCODE, - value, mask); - if (err) - goto out; - } - - /* The IS2 ARP keyset does not support ARP hardware addresses */ - if (!is_zero_ether_addr(mt.mask->sha) || - !is_zero_ether_addr(mt.mask->tha)) { - err = -EINVAL; - goto out; - } - - if (mt.mask->sip) { - ipval = be32_to_cpu((__force __be32)mt.key->sip); - ipmsk = be32_to_cpu((__force __be32)mt.mask->sip); - - err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_SIP, - ipval, ipmsk); - if (err) - goto out; - } - - if (mt.mask->tip) { - ipval = be32_to_cpu((__force __be32)mt.key->tip); - ipmsk = be32_to_cpu((__force __be32)mt.mask->tip); - - err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_DIP, - ipval, ipmsk); - if (err) - goto out; + if (st->admin->vtype != VCAP_TYPE_IS0) { + NL_SET_ERR_MSG_MOD(st->fco->common.extack, + "cvlan not supported in this VCAP"); + return -EINVAL; } - st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ARP); - - return 0; - -out: - NL_SET_ERR_MSG_MOD(st->fco->common.extack, "arp parse error"); - return err; + return vcap_tc_flower_handler_cvlan_usage(st); } static int -sparx5_tc_flower_handler_ip_usage(struct sparx5_tc_flower_parse_usage *st) +sparx5_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st) { - struct flow_match_ip mt; - int err = 0; - - flow_rule_match_ip(st->frule, &mt); + enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS; + enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS; - if (mt.mask->tos) { - err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_TOS, - mt.key->tos, - mt.mask->tos); - if (err) - goto out; + if (st->admin->vtype == VCAP_TYPE_IS0) { + vid_key = VCAP_KF_8021Q_VID0; + pcp_key = VCAP_KF_8021Q_PCP0; } - st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IP); - - return err; - -out: - NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_tos parse error"); - return err; + return vcap_tc_flower_handler_vlan_usage(st, vid_key, pcp_key); } -static int (*sparx5_tc_flower_usage_handlers[])(struct sparx5_tc_flower_parse_usage *st) = { - [FLOW_DISSECTOR_KEY_ETH_ADDRS] = sparx5_tc_flower_handler_ethaddr_usage, - [FLOW_DISSECTOR_KEY_IPV4_ADDRS] = sparx5_tc_flower_handler_ipv4_usage, - [FLOW_DISSECTOR_KEY_IPV6_ADDRS] = sparx5_tc_flower_handler_ipv6_usage, +static int (*sparx5_tc_flower_usage_handlers[])(struct vcap_tc_flower_parse_usage *st) = { + [FLOW_DISSECTOR_KEY_ETH_ADDRS] = vcap_tc_flower_handler_ethaddr_usage, + [FLOW_DISSECTOR_KEY_IPV4_ADDRS] = vcap_tc_flower_handler_ipv4_usage, + [FLOW_DISSECTOR_KEY_IPV6_ADDRS] = vcap_tc_flower_handler_ipv6_usage, [FLOW_DISSECTOR_KEY_CONTROL] = sparx5_tc_flower_handler_control_usage, - [FLOW_DISSECTOR_KEY_PORTS] = sparx5_tc_flower_handler_portnum_usage, + [FLOW_DISSECTOR_KEY_PORTS] = vcap_tc_flower_handler_portnum_usage, [FLOW_DISSECTOR_KEY_BASIC] = sparx5_tc_flower_handler_basic_usage, [FLOW_DISSECTOR_KEY_CVLAN] = sparx5_tc_flower_handler_cvlan_usage, [FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage, - [FLOW_DISSECTOR_KEY_TCP] = sparx5_tc_flower_handler_tcp_usage, - [FLOW_DISSECTOR_KEY_ARP] = sparx5_tc_flower_handler_arp_usage, - [FLOW_DISSECTOR_KEY_IP] = sparx5_tc_flower_handler_ip_usage, + [FLOW_DISSECTOR_KEY_TCP] = vcap_tc_flower_handler_tcp_usage, + [FLOW_DISSECTOR_KEY_ARP] = vcap_tc_flower_handler_arp_usage, + [FLOW_DISSECTOR_KEY_IP] = vcap_tc_flower_handler_ip_usage, }; static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco, @@ -575,7 +196,7 @@ static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco, struct vcap_rule *vrule, u16 *l3_proto) { - struct sparx5_tc_flower_parse_usage state = { + struct vcap_tc_flower_parse_usage state = { .fco = fco, .vrule = vrule, .l3_proto = ETH_P_ALL, @@ -607,7 +228,8 @@ static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco, static int sparx5_tc_flower_action_check(struct vcap_control *vctrl, struct net_device *ndev, - struct flow_cls_offload *fco) + struct flow_cls_offload *fco, + bool ingress) { struct flow_rule *rule = flow_cls_offload_flow_rule(fco); struct flow_action_entry *actent, *last_actent = NULL; @@ -644,7 +266,8 @@ static int sparx5_tc_flower_action_check(struct vcap_control *vctrl, "Invalid goto chain"); return -EINVAL; } - } else if (!vcap_is_last_chain(vctrl, fco->common.chain_index)) { + } else if (!vcap_is_last_chain(vctrl, fco->common.chain_index, + ingress)) { NL_SET_ERR_MSG_MOD(fco->common.extack, "Last action must be 'goto'"); return -EINVAL; @@ -667,7 +290,7 @@ static int sparx5_tc_add_rule_counter(struct vcap_admin *admin, { int err; - if (admin->vtype == VCAP_TYPE_IS2) { + if (admin->vtype == VCAP_TYPE_IS2 || admin->vtype == VCAP_TYPE_ES2) { err = vcap_rule_mod_action_u32(vrule, VCAP_AF_CNT_ID, vrule->id); if (err) @@ -870,6 +493,9 @@ static int sparx5_tc_set_actionset(struct vcap_admin *admin, case VCAP_TYPE_IS2: aset = VCAP_AFS_BASE_TYPE; break; + case VCAP_TYPE_ES2: + aset = VCAP_AFS_BASE_TYPE; + break; default: return -EINVAL; } @@ -906,6 +532,10 @@ static int sparx5_tc_add_rule_link_target(struct vcap_admin *admin, return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_PAG, link_val, /* target */ ~0); + case VCAP_TYPE_ES2: + /* Add ISDX key for chaining rules from IS0 */ + return vcap_rule_add_key_u32(vrule, VCAP_KF_ISDX_CLS, link_val, + ~0); default: break; } @@ -948,6 +578,18 @@ static int sparx5_tc_add_rule_link(struct vcap_control *vctrl, 0xff); if (err) goto out; + } else if (admin->vtype == VCAP_TYPE_IS0 && + to_admin->vtype == VCAP_TYPE_ES2) { + /* Between IS0 and ES2 the ISDX value is used */ + err = vcap_rule_add_action_u32(vrule, VCAP_AF_ISDX_VAL, + diff); + if (err) + goto out; + err = vcap_rule_add_action_bit(vrule, + VCAP_AF_ISDX_ADD_REPLACE_SEL, + VCAP_BIT_1); + if (err) + goto out; } else { pr_err("%s:%d: unsupported chain destination: %d\n", __func__, __LINE__, to_cid); @@ -957,22 +599,160 @@ out: return err; } +static int sparx5_tc_flower_parse_act_gate(struct sparx5_psfp_sg *sg, + struct flow_action_entry *act, + struct netlink_ext_ack *extack) +{ + int i; + + if (act->gate.prio < -1 || act->gate.prio > SPX5_PSFP_SG_MAX_IPV) { + NL_SET_ERR_MSG_MOD(extack, "Invalid gate priority"); + return -EINVAL; + } + + if (act->gate.cycletime < SPX5_PSFP_SG_MIN_CYCLE_TIME_NS || + act->gate.cycletime > SPX5_PSFP_SG_MAX_CYCLE_TIME_NS) { + NL_SET_ERR_MSG_MOD(extack, "Invalid gate cycletime"); + return -EINVAL; + } + + if (act->gate.cycletimeext > SPX5_PSFP_SG_MAX_CYCLE_TIME_NS) { + NL_SET_ERR_MSG_MOD(extack, "Invalid gate cycletimeext"); + return -EINVAL; + } + + if (act->gate.num_entries >= SPX5_PSFP_GCE_CNT) { + NL_SET_ERR_MSG_MOD(extack, "Invalid number of gate entries"); + return -EINVAL; + } + + sg->gate_state = true; + sg->ipv = act->gate.prio; + sg->num_entries = act->gate.num_entries; + sg->cycletime = act->gate.cycletime; + sg->cycletimeext = act->gate.cycletimeext; + + for (i = 0; i < sg->num_entries; i++) { + sg->gce[i].gate_state = !!act->gate.entries[i].gate_state; + sg->gce[i].interval = act->gate.entries[i].interval; + sg->gce[i].ipv = act->gate.entries[i].ipv; + sg->gce[i].maxoctets = act->gate.entries[i].maxoctets; + } + + return 0; +} + +static int sparx5_tc_flower_parse_act_police(struct sparx5_policer *pol, + struct flow_action_entry *act, + struct netlink_ext_ack *extack) +{ + pol->type = SPX5_POL_SERVICE; + pol->rate = div_u64(act->police.rate_bytes_ps, 1000) * 8; + pol->burst = act->police.burst; + pol->idx = act->hw_index; + + /* rate is now in kbit */ + if (pol->rate > DIV_ROUND_UP(SPX5_SDLB_GROUP_RATE_MAX, 1000)) { + NL_SET_ERR_MSG_MOD(extack, "Maximum rate exceeded"); + return -EINVAL; + } + + if (act->police.exceed.act_id != FLOW_ACTION_DROP) { + NL_SET_ERR_MSG_MOD(extack, "Offload not supported when exceed action is not drop"); + return -EOPNOTSUPP; + } + + if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && + act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { + NL_SET_ERR_MSG_MOD(extack, "Offload not supported when conform action is not pipe or ok"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int sparx5_tc_flower_psfp_setup(struct sparx5 *sparx5, + struct vcap_rule *vrule, int sg_idx, + int pol_idx, struct sparx5_psfp_sg *sg, + struct sparx5_psfp_fm *fm, + struct sparx5_psfp_sf *sf) +{ + u32 psfp_sfid = 0, psfp_fmid = 0, psfp_sgid = 0; + int ret; + + /* Must always have a stream gate - max sdu (filter option) is evaluated + * after frames have passed the gate, so in case of only a policer, we + * allocate a stream gate that is always open. + */ + if (sg_idx < 0) { + sg_idx = sparx5_pool_idx_to_id(SPX5_PSFP_SG_OPEN); + sg->ipv = 0; /* Disabled */ + sg->cycletime = SPX5_PSFP_SG_CYCLE_TIME_DEFAULT; + sg->num_entries = 1; + sg->gate_state = 1; /* Open */ + sg->gate_enabled = 1; + sg->gce[0].gate_state = 1; + sg->gce[0].interval = SPX5_PSFP_SG_CYCLE_TIME_DEFAULT; + sg->gce[0].ipv = 0; + sg->gce[0].maxoctets = 0; /* Disabled */ + } + + ret = sparx5_psfp_sg_add(sparx5, sg_idx, sg, &psfp_sgid); + if (ret < 0) + return ret; + + if (pol_idx >= 0) { + /* Add new flow-meter */ + ret = sparx5_psfp_fm_add(sparx5, pol_idx, fm, &psfp_fmid); + if (ret < 0) + return ret; + } + + /* Map stream filter to stream gate */ + sf->sgid = psfp_sgid; + + /* Add new stream-filter and map it to a steam gate */ + ret = sparx5_psfp_sf_add(sparx5, sf, &psfp_sfid); + if (ret < 0) + return ret; + + /* Streams are classified by ISDX - map ISDX 1:1 to sfid for now. */ + sparx5_isdx_conf_set(sparx5, psfp_sfid, psfp_sfid, psfp_fmid); + + ret = vcap_rule_add_action_bit(vrule, VCAP_AF_ISDX_ADD_REPLACE_SEL, + VCAP_BIT_1); + if (ret) + return ret; + + ret = vcap_rule_add_action_u32(vrule, VCAP_AF_ISDX_VAL, psfp_sfid); + if (ret) + return ret; + + return 0; +} + static int sparx5_tc_flower_replace(struct net_device *ndev, struct flow_cls_offload *fco, - struct vcap_admin *admin) + struct vcap_admin *admin, + bool ingress) { + struct sparx5_psfp_sf sf = { .max_sdu = SPX5_PSFP_SF_MAX_SDU }; + struct netlink_ext_ack *extack = fco->common.extack; + int err, idx, tc_sg_idx = -1, tc_pol_idx = -1; struct sparx5_port *port = netdev_priv(ndev); struct sparx5_multiple_rules multi = {}; + struct sparx5 *sparx5 = port->sparx5; + struct sparx5_psfp_sg sg = { 0 }; + struct sparx5_psfp_fm fm = { 0 }; struct flow_action_entry *act; struct vcap_control *vctrl; struct flow_rule *frule; struct vcap_rule *vrule; u16 l3_proto; - int err, idx; vctrl = port->sparx5->vcap_ctrl; - err = sparx5_tc_flower_action_check(vctrl, ndev, fco); + err = sparx5_tc_flower_action_check(vctrl, ndev, fco, ingress); if (err) return err; @@ -1000,8 +780,29 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, frule = flow_cls_offload_flow_rule(fco); flow_action_for_each(idx, act, &frule->action) { switch (act->id) { + case FLOW_ACTION_GATE: { + err = sparx5_tc_flower_parse_act_gate(&sg, act, extack); + if (err < 0) + goto out; + + tc_sg_idx = act->hw_index; + + break; + } + case FLOW_ACTION_POLICE: { + err = sparx5_tc_flower_parse_act_police(&fm.pol, act, + extack); + if (err < 0) + goto out; + + tc_pol_idx = fm.pol.idx; + sf.max_sdu = act->police.mtu; + + break; + } case FLOW_ACTION_TRAP: - if (admin->vtype != VCAP_TYPE_IS2) { + if (admin->vtype != VCAP_TYPE_IS2 && + admin->vtype != VCAP_TYPE_ES2) { NL_SET_ERR_MSG_MOD(fco->common.extack, "Trap action not supported in this VCAP"); err = -EOPNOTSUPP; @@ -1016,8 +817,11 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, VCAP_AF_CPU_QUEUE_NUM, 0); if (err) goto out; - err = vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE, - SPX5_PMM_REPLACE_ALL); + if (admin->vtype != VCAP_TYPE_IS2) + break; + err = vcap_rule_add_action_u32(vrule, + VCAP_AF_MASK_MODE, + SPX5_PMM_REPLACE_ALL); if (err) goto out; break; @@ -1042,6 +846,14 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, } } + /* Setup PSFP */ + if (tc_sg_idx >= 0 || tc_pol_idx >= 0) { + err = sparx5_tc_flower_psfp_setup(sparx5, vrule, tc_sg_idx, + tc_pol_idx, &sg, &fm, &sf); + if (err) + goto out; + } + err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin, l3_proto, &multi); if (err) { @@ -1070,19 +882,86 @@ out: return err; } +static void sparx5_tc_free_psfp_resources(struct sparx5 *sparx5, + struct vcap_rule *vrule) +{ + struct vcap_client_actionfield *afield; + u32 isdx, sfid, sgid, fmid; + + /* Check if VCAP_AF_ISDX_VAL action is set for this rule - and if + * it is used for stream and/or flow-meter classification. + */ + afield = vcap_find_actionfield(vrule, VCAP_AF_ISDX_VAL); + if (!afield) + return; + + isdx = afield->data.u32.value; + sfid = sparx5_psfp_isdx_get_sf(sparx5, isdx); + + if (!sfid) + return; + + fmid = sparx5_psfp_isdx_get_fm(sparx5, isdx); + sgid = sparx5_psfp_sf_get_sg(sparx5, sfid); + + if (fmid && sparx5_psfp_fm_del(sparx5, fmid) < 0) + pr_err("%s:%d Could not delete invalid fmid: %d", __func__, + __LINE__, fmid); + + if (sgid && sparx5_psfp_sg_del(sparx5, sgid) < 0) + pr_err("%s:%d Could not delete invalid sgid: %d", __func__, + __LINE__, sgid); + + if (sparx5_psfp_sf_del(sparx5, sfid) < 0) + pr_err("%s:%d Could not delete invalid sfid: %d", __func__, + __LINE__, sfid); + + sparx5_isdx_conf_set(sparx5, isdx, 0, 0); +} + +static int sparx5_tc_free_rule_resources(struct net_device *ndev, + struct vcap_control *vctrl, + int rule_id) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + struct vcap_rule *vrule; + int ret = 0; + + vrule = vcap_get_rule(vctrl, rule_id); + if (!vrule || IS_ERR(vrule)) + return -EINVAL; + + sparx5_tc_free_psfp_resources(sparx5, vrule); + + vcap_free_rule(vrule); + return ret; +} + static int sparx5_tc_flower_destroy(struct net_device *ndev, struct flow_cls_offload *fco, struct vcap_admin *admin) { struct sparx5_port *port = netdev_priv(ndev); + int err = -ENOENT, count = 0, rule_id; struct vcap_control *vctrl; - int err = -ENOENT, rule_id; vctrl = port->sparx5->vcap_ctrl; while (true) { rule_id = vcap_lookup_rule_by_cookie(vctrl, fco->cookie); if (rule_id <= 0) break; + if (count == 0) { + /* Resources are attached to the first rule of + * a set of rules. Only works if the rules are + * in the correct order. + */ + err = sparx5_tc_free_rule_resources(ndev, vctrl, + rule_id); + if (err) + pr_err("%s:%d: could not free resources %d\n", + __func__, __LINE__, rule_id); + } err = vcap_del_rule(vctrl, ndev, rule_id); if (err) { pr_err("%s:%d: could not delete rule %d\n", @@ -1130,7 +1009,7 @@ int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco, switch (fco->command) { case FLOW_CLS_REPLACE: - return sparx5_tc_flower_replace(ndev, fco, admin); + return sparx5_tc_flower_replace(ndev, fco, admin, ingress); case FLOW_CLS_DESTROY: return sparx5_tc_flower_destroy(ndev, fco, admin); case FLOW_CLS_STATS: |