diff --git a/drivers/net/ethernet/mscc/ocelot_ace.c b/drivers/net/ethernet/mscc/ocelot_ace.c index 3bd286044480..8a2f7d13ef6d 100644 --- a/drivers/net/ethernet/mscc/ocelot_ace.c +++ b/drivers/net/ethernet/mscc/ocelot_ace.c @@ -706,13 +706,114 @@ ocelot_ace_rule_get_rule_index(struct ocelot_acl_block *block, int index) return NULL; } +/* If @on=false, then SNAP, ARP, IP and OAM frames will not match on keys based + * on destination and source MAC addresses, but only on higher-level protocol + * information. The only frame types to match on keys containing MAC addresses + * in this case are non-SNAP, non-ARP, non-IP and non-OAM frames. + * + * If @on=true, then the above frame types (SNAP, ARP, IP and OAM) will match + * on MAC_ETYPE keys such as destination and source MAC on this ingress port. + * However the setting has the side effect of making these frames not matching + * on any _other_ keys than MAC_ETYPE ones. + */ +static void ocelot_match_all_as_mac_etype(struct ocelot *ocelot, int port, + bool on) +{ + u32 val = 0; + + if (on) + val = ANA_PORT_VCAP_S2_CFG_S2_SNAP_DIS(3) | + ANA_PORT_VCAP_S2_CFG_S2_ARP_DIS(3) | + ANA_PORT_VCAP_S2_CFG_S2_IP_TCPUDP_DIS(3) | + ANA_PORT_VCAP_S2_CFG_S2_IP_OTHER_DIS(3) | + ANA_PORT_VCAP_S2_CFG_S2_OAM_DIS(3); + + ocelot_rmw_gix(ocelot, val, + ANA_PORT_VCAP_S2_CFG_S2_SNAP_DIS_M | + ANA_PORT_VCAP_S2_CFG_S2_ARP_DIS_M | + ANA_PORT_VCAP_S2_CFG_S2_IP_TCPUDP_DIS_M | + ANA_PORT_VCAP_S2_CFG_S2_IP_OTHER_DIS_M | + ANA_PORT_VCAP_S2_CFG_S2_OAM_DIS_M, + ANA_PORT_VCAP_S2_CFG, port); +} + +static bool ocelot_ace_is_problematic_mac_etype(struct ocelot_ace_rule *ace) +{ + if (ace->type != OCELOT_ACE_TYPE_ETYPE) + return false; + if (ether_addr_to_u64(ace->frame.etype.dmac.value) & + ether_addr_to_u64(ace->frame.etype.dmac.mask)) + return true; + if (ether_addr_to_u64(ace->frame.etype.smac.value) & + ether_addr_to_u64(ace->frame.etype.smac.mask)) + return true; + return false; +} + +static bool ocelot_ace_is_problematic_non_mac_etype(struct ocelot_ace_rule *ace) +{ + if (ace->type == OCELOT_ACE_TYPE_SNAP) + return true; + if (ace->type == OCELOT_ACE_TYPE_ARP) + return true; + if (ace->type == OCELOT_ACE_TYPE_IPV4) + return true; + if (ace->type == OCELOT_ACE_TYPE_IPV6) + return true; + return false; +} + +static bool ocelot_exclusive_mac_etype_ace_rules(struct ocelot *ocelot, + struct ocelot_ace_rule *ace) +{ + struct ocelot_acl_block *block = &ocelot->acl_block; + struct ocelot_ace_rule *tmp; + unsigned long port; + int i; + + if (ocelot_ace_is_problematic_mac_etype(ace)) { + /* Search for any non-MAC_ETYPE rules on the port */ + for (i = 0; i < block->count; i++) { + tmp = ocelot_ace_rule_get_rule_index(block, i); + if (tmp->ingress_port_mask & ace->ingress_port_mask && + ocelot_ace_is_problematic_non_mac_etype(tmp)) + return false; + } + + for_each_set_bit(port, &ace->ingress_port_mask, + ocelot->num_phys_ports) + ocelot_match_all_as_mac_etype(ocelot, port, true); + } else if (ocelot_ace_is_problematic_non_mac_etype(ace)) { + /* Search for any MAC_ETYPE rules on the port */ + for (i = 0; i < block->count; i++) { + tmp = ocelot_ace_rule_get_rule_index(block, i); + if (tmp->ingress_port_mask & ace->ingress_port_mask && + ocelot_ace_is_problematic_mac_etype(tmp)) + return false; + } + + for_each_set_bit(port, &ace->ingress_port_mask, + ocelot->num_phys_ports) + ocelot_match_all_as_mac_etype(ocelot, port, false); + } + + return true; +} + int ocelot_ace_rule_offload_add(struct ocelot *ocelot, - struct ocelot_ace_rule *rule) + struct ocelot_ace_rule *rule, + struct netlink_ext_ack *extack) { struct ocelot_acl_block *block = &ocelot->acl_block; struct ocelot_ace_rule *ace; int i, index; + if (!ocelot_exclusive_mac_etype_ace_rules(ocelot, rule)) { + NL_SET_ERR_MSG_MOD(extack, + "Cannot mix MAC_ETYPE with non-MAC_ETYPE rules"); + return -EBUSY; + } + /* Add rule to the linked list */ ocelot_ace_rule_add(ocelot, block, rule); diff --git a/drivers/net/ethernet/mscc/ocelot_ace.h b/drivers/net/ethernet/mscc/ocelot_ace.h index 29d22c566786..099e177f2617 100644 --- a/drivers/net/ethernet/mscc/ocelot_ace.h +++ b/drivers/net/ethernet/mscc/ocelot_ace.h @@ -194,7 +194,7 @@ struct ocelot_ace_rule { enum ocelot_ace_action action; struct ocelot_ace_stats stats; - u16 ingress_port_mask; + unsigned long ingress_port_mask; enum ocelot_vcap_bit dmac_mc; enum ocelot_vcap_bit dmac_bc; @@ -215,7 +215,8 @@ struct ocelot_ace_rule { }; int ocelot_ace_rule_offload_add(struct ocelot *ocelot, - struct ocelot_ace_rule *rule); + struct ocelot_ace_rule *rule, + struct netlink_ext_ack *extack); int ocelot_ace_rule_offload_del(struct ocelot *ocelot, struct ocelot_ace_rule *rule); int ocelot_ace_rule_stats_update(struct ocelot *ocelot, diff --git a/drivers/net/ethernet/mscc/ocelot_flower.c b/drivers/net/ethernet/mscc/ocelot_flower.c index 341923311fec..954cb67eeaa2 100644 --- a/drivers/net/ethernet/mscc/ocelot_flower.c +++ b/drivers/net/ethernet/mscc/ocelot_flower.c @@ -205,7 +205,7 @@ int ocelot_cls_flower_replace(struct ocelot *ocelot, int port, return ret; } - return ocelot_ace_rule_offload_add(ocelot, ace); + return ocelot_ace_rule_offload_add(ocelot, ace, f->common.extack); } EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);