2019-05-31 09:16:57 +02:00
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/* Microsemi Ocelot Switch driver
* Copyright ( c ) 2019 Microsemi Corporation
*/
# include <net/pkt_cls.h>
# include <net/tc_act/tc_gact.h>
# include "ocelot_ace.h"
struct ocelot_port_block {
struct ocelot_acl_block * block ;
2019-11-09 15:02:53 +02:00
struct ocelot_port_private * priv ;
2019-05-31 09:16:57 +02:00
} ;
2019-07-09 22:55:49 +02:00
static int ocelot_flower_parse_action ( struct flow_cls_offload * f ,
2019-05-31 09:16:57 +02:00
struct ocelot_ace_rule * rule )
{
const struct flow_action_entry * a ;
int i ;
if ( f - > rule - > action . num_entries ! = 1 )
return - EOPNOTSUPP ;
flow_action_for_each ( i , a , & f - > rule - > action ) {
switch ( a - > id ) {
case FLOW_ACTION_DROP :
rule - > action = OCELOT_ACL_ACTION_DROP ;
break ;
case FLOW_ACTION_TRAP :
rule - > action = OCELOT_ACL_ACTION_TRAP ;
break ;
default :
return - EOPNOTSUPP ;
}
}
return 0 ;
}
2019-07-09 22:55:49 +02:00
static int ocelot_flower_parse ( struct flow_cls_offload * f ,
2019-05-31 09:16:57 +02:00
struct ocelot_ace_rule * ocelot_rule )
{
2019-07-09 22:55:49 +02:00
struct flow_rule * rule = flow_cls_offload_flow_rule ( f ) ;
2019-05-31 09:16:57 +02:00
struct flow_dissector * dissector = rule - > match . dissector ;
if ( dissector - > used_keys &
~ ( BIT ( FLOW_DISSECTOR_KEY_CONTROL ) |
BIT ( FLOW_DISSECTOR_KEY_BASIC ) |
BIT ( FLOW_DISSECTOR_KEY_PORTS ) |
BIT ( FLOW_DISSECTOR_KEY_VLAN ) |
BIT ( FLOW_DISSECTOR_KEY_IPV4_ADDRS ) |
BIT ( FLOW_DISSECTOR_KEY_IPV6_ADDRS ) |
BIT ( FLOW_DISSECTOR_KEY_ETH_ADDRS ) ) ) {
return - EOPNOTSUPP ;
}
if ( flow_rule_match_key ( rule , FLOW_DISSECTOR_KEY_CONTROL ) ) {
struct flow_match_control match ;
flow_rule_match_control ( rule , & match ) ;
}
if ( flow_rule_match_key ( rule , FLOW_DISSECTOR_KEY_ETH_ADDRS ) ) {
struct flow_match_eth_addrs match ;
u16 proto = ntohs ( f - > common . protocol ) ;
/* The hw support mac matches only for MAC_ETYPE key,
* therefore if other matches ( port , tcp flags , etc ) are added
* then just bail out
*/
if ( ( dissector - > used_keys &
( BIT ( FLOW_DISSECTOR_KEY_ETH_ADDRS ) |
BIT ( FLOW_DISSECTOR_KEY_BASIC ) |
BIT ( FLOW_DISSECTOR_KEY_CONTROL ) ) ) ! =
( BIT ( FLOW_DISSECTOR_KEY_ETH_ADDRS ) |
BIT ( FLOW_DISSECTOR_KEY_BASIC ) |
BIT ( FLOW_DISSECTOR_KEY_CONTROL ) ) )
return - EOPNOTSUPP ;
if ( proto = = ETH_P_IP | |
proto = = ETH_P_IPV6 | |
proto = = ETH_P_ARP )
return - EOPNOTSUPP ;
flow_rule_match_eth_addrs ( rule , & match ) ;
ocelot_rule - > type = OCELOT_ACE_TYPE_ETYPE ;
ether_addr_copy ( ocelot_rule - > frame . etype . dmac . value ,
match . key - > dst ) ;
ether_addr_copy ( ocelot_rule - > frame . etype . smac . value ,
match . key - > src ) ;
ether_addr_copy ( ocelot_rule - > frame . etype . dmac . mask ,
match . mask - > dst ) ;
ether_addr_copy ( ocelot_rule - > frame . etype . smac . mask ,
match . mask - > src ) ;
goto finished_key_parsing ;
}
if ( flow_rule_match_key ( rule , FLOW_DISSECTOR_KEY_BASIC ) ) {
struct flow_match_basic match ;
flow_rule_match_basic ( rule , & match ) ;
if ( ntohs ( match . key - > n_proto ) = = ETH_P_IP ) {
ocelot_rule - > type = OCELOT_ACE_TYPE_IPV4 ;
ocelot_rule - > frame . ipv4 . proto . value [ 0 ] =
match . key - > ip_proto ;
ocelot_rule - > frame . ipv4 . proto . mask [ 0 ] =
match . mask - > ip_proto ;
}
if ( ntohs ( match . key - > n_proto ) = = ETH_P_IPV6 ) {
ocelot_rule - > type = OCELOT_ACE_TYPE_IPV6 ;
ocelot_rule - > frame . ipv6 . proto . value [ 0 ] =
match . key - > ip_proto ;
ocelot_rule - > frame . ipv6 . proto . mask [ 0 ] =
match . mask - > ip_proto ;
}
}
if ( flow_rule_match_key ( rule , FLOW_DISSECTOR_KEY_IPV4_ADDRS ) & &
ntohs ( f - > common . protocol ) = = ETH_P_IP ) {
struct flow_match_ipv4_addrs match ;
u8 * tmp ;
flow_rule_match_ipv4_addrs ( rule , & match ) ;
tmp = & ocelot_rule - > frame . ipv4 . sip . value . addr [ 0 ] ;
memcpy ( tmp , & match . key - > src , 4 ) ;
tmp = & ocelot_rule - > frame . ipv4 . sip . mask . addr [ 0 ] ;
memcpy ( tmp , & match . mask - > src , 4 ) ;
tmp = & ocelot_rule - > frame . ipv4 . dip . value . addr [ 0 ] ;
memcpy ( tmp , & match . key - > dst , 4 ) ;
tmp = & ocelot_rule - > frame . ipv4 . dip . mask . addr [ 0 ] ;
memcpy ( tmp , & match . mask - > dst , 4 ) ;
}
if ( flow_rule_match_key ( rule , FLOW_DISSECTOR_KEY_IPV6_ADDRS ) & &
ntohs ( f - > common . protocol ) = = ETH_P_IPV6 ) {
return - EOPNOTSUPP ;
}
if ( flow_rule_match_key ( rule , FLOW_DISSECTOR_KEY_PORTS ) ) {
struct flow_match_ports match ;
flow_rule_match_ports ( rule , & match ) ;
ocelot_rule - > frame . ipv4 . sport . value = ntohs ( match . key - > src ) ;
ocelot_rule - > frame . ipv4 . sport . mask = ntohs ( match . mask - > src ) ;
ocelot_rule - > frame . ipv4 . dport . value = ntohs ( match . key - > dst ) ;
ocelot_rule - > frame . ipv4 . dport . mask = ntohs ( match . mask - > dst ) ;
}
if ( flow_rule_match_key ( rule , FLOW_DISSECTOR_KEY_VLAN ) ) {
struct flow_match_vlan match ;
flow_rule_match_vlan ( rule , & match ) ;
ocelot_rule - > type = OCELOT_ACE_TYPE_ANY ;
ocelot_rule - > vlan . vid . value = match . key - > vlan_id ;
ocelot_rule - > vlan . vid . mask = match . mask - > vlan_id ;
ocelot_rule - > vlan . pcp . value [ 0 ] = match . key - > vlan_priority ;
ocelot_rule - > vlan . pcp . mask [ 0 ] = match . mask - > vlan_priority ;
}
finished_key_parsing :
2019-08-16 03:24:09 +02:00
ocelot_rule - > prio = f - > common . prio ;
2019-05-31 09:16:57 +02:00
ocelot_rule - > id = f - > cookie ;
return ocelot_flower_parse_action ( f , ocelot_rule ) ;
}
static
2019-07-09 22:55:49 +02:00
struct ocelot_ace_rule * ocelot_ace_rule_create ( struct flow_cls_offload * f ,
2019-05-31 09:16:57 +02:00
struct ocelot_port_block * block )
{
struct ocelot_ace_rule * rule ;
rule = kzalloc ( sizeof ( * rule ) , GFP_KERNEL ) ;
if ( ! rule )
return NULL ;
2019-11-09 15:02:53 +02:00
rule - > port = & block - > priv - > port ;
rule - > chip_port = block - > priv - > chip_port ;
2019-05-31 09:16:57 +02:00
return rule ;
}
2019-07-09 22:55:49 +02:00
static int ocelot_flower_replace ( struct flow_cls_offload * f ,
2019-05-31 09:16:57 +02:00
struct ocelot_port_block * port_block )
{
struct ocelot_ace_rule * rule ;
int ret ;
rule = ocelot_ace_rule_create ( f , port_block ) ;
if ( ! rule )
return - ENOMEM ;
ret = ocelot_flower_parse ( f , rule ) ;
if ( ret ) {
kfree ( rule ) ;
return ret ;
}
ret = ocelot_ace_rule_offload_add ( rule ) ;
if ( ret )
return ret ;
2019-11-09 15:02:53 +02:00
port_block - > priv - > tc . offload_cnt + + ;
2019-05-31 09:16:57 +02:00
return 0 ;
}
2019-07-09 22:55:49 +02:00
static int ocelot_flower_destroy ( struct flow_cls_offload * f ,
2019-05-31 09:16:57 +02:00
struct ocelot_port_block * port_block )
{
struct ocelot_ace_rule rule ;
int ret ;
2019-08-16 03:24:09 +02:00
rule . prio = f - > common . prio ;
2019-11-09 15:02:53 +02:00
rule . port = & port_block - > priv - > port ;
2019-05-31 09:16:57 +02:00
rule . id = f - > cookie ;
ret = ocelot_ace_rule_offload_del ( & rule ) ;
if ( ret )
return ret ;
2019-11-09 15:02:53 +02:00
port_block - > priv - > tc . offload_cnt - - ;
2019-05-31 09:16:57 +02:00
return 0 ;
}
2019-07-09 22:55:49 +02:00
static int ocelot_flower_stats_update ( struct flow_cls_offload * f ,
2019-05-31 09:16:57 +02:00
struct ocelot_port_block * port_block )
{
struct ocelot_ace_rule rule ;
int ret ;
2019-08-16 03:24:09 +02:00
rule . prio = f - > common . prio ;
2019-11-09 15:02:53 +02:00
rule . port = & port_block - > priv - > port ;
2019-05-31 09:16:57 +02:00
rule . id = f - > cookie ;
ret = ocelot_ace_rule_stats_update ( & rule ) ;
if ( ret )
return ret ;
flow_stats_update ( & f - > stats , 0x0 , rule . stats . pkts , 0x0 ) ;
return 0 ;
}
2019-07-09 22:55:49 +02:00
static int ocelot_setup_tc_cls_flower ( struct flow_cls_offload * f ,
2019-05-31 09:16:57 +02:00
struct ocelot_port_block * port_block )
{
switch ( f - > command ) {
2019-07-09 22:55:49 +02:00
case FLOW_CLS_REPLACE :
2019-05-31 09:16:57 +02:00
return ocelot_flower_replace ( f , port_block ) ;
2019-07-09 22:55:49 +02:00
case FLOW_CLS_DESTROY :
2019-05-31 09:16:57 +02:00
return ocelot_flower_destroy ( f , port_block ) ;
2019-07-09 22:55:49 +02:00
case FLOW_CLS_STATS :
2019-05-31 09:16:57 +02:00
return ocelot_flower_stats_update ( f , port_block ) ;
default :
return - EOPNOTSUPP ;
}
}
static int ocelot_setup_tc_block_cb_flower ( enum tc_setup_type type ,
void * type_data , void * cb_priv )
{
struct ocelot_port_block * port_block = cb_priv ;
2019-11-09 15:02:53 +02:00
if ( ! tc_cls_can_offload_and_chain0 ( port_block - > priv - > dev , type_data ) )
2019-05-31 09:16:57 +02:00
return - EOPNOTSUPP ;
switch ( type ) {
case TC_SETUP_CLSFLOWER :
return ocelot_setup_tc_cls_flower ( type_data , cb_priv ) ;
case TC_SETUP_CLSMATCHALL :
return 0 ;
default :
return - EOPNOTSUPP ;
}
}
static struct ocelot_port_block *
2019-11-09 15:02:53 +02:00
ocelot_port_block_create ( struct ocelot_port_private * priv )
2019-05-31 09:16:57 +02:00
{
struct ocelot_port_block * port_block ;
port_block = kzalloc ( sizeof ( * port_block ) , GFP_KERNEL ) ;
if ( ! port_block )
return NULL ;
2019-11-09 15:02:53 +02:00
port_block - > priv = priv ;
2019-05-31 09:16:57 +02:00
return port_block ;
}
static void ocelot_port_block_destroy ( struct ocelot_port_block * block )
{
kfree ( block ) ;
}
2019-07-09 22:55:46 +02:00
static void ocelot_tc_block_unbind ( void * cb_priv )
{
struct ocelot_port_block * port_block = cb_priv ;
ocelot_port_block_destroy ( port_block ) ;
}
2019-11-09 15:02:53 +02:00
int ocelot_setup_tc_block_flower_bind ( struct ocelot_port_private * priv ,
2019-07-09 22:55:46 +02:00
struct flow_block_offload * f )
2019-05-31 09:16:57 +02:00
{
struct ocelot_port_block * port_block ;
2019-07-09 22:55:46 +02:00
struct flow_block_cb * block_cb ;
2019-05-31 09:16:57 +02:00
int ret ;
2019-07-09 22:55:41 +02:00
if ( f - > binder_type = = FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS )
2019-05-31 09:16:57 +02:00
return - EOPNOTSUPP ;
2019-07-19 18:20:16 +02:00
block_cb = flow_block_cb_lookup ( f - > block ,
2019-11-09 15:02:53 +02:00
ocelot_setup_tc_block_cb_flower , priv ) ;
2019-05-31 09:16:57 +02:00
if ( ! block_cb ) {
2019-11-09 15:02:53 +02:00
port_block = ocelot_port_block_create ( priv ) ;
2019-05-31 09:16:57 +02:00
if ( ! port_block )
return - ENOMEM ;
2019-07-19 18:20:14 +02:00
block_cb = flow_block_cb_alloc ( ocelot_setup_tc_block_cb_flower ,
2019-11-09 15:02:53 +02:00
priv , port_block ,
2019-07-09 22:55:46 +02:00
ocelot_tc_block_unbind ) ;
2019-05-31 09:16:57 +02:00
if ( IS_ERR ( block_cb ) ) {
ret = PTR_ERR ( block_cb ) ;
goto err_cb_register ;
}
2019-07-09 22:55:46 +02:00
flow_block_cb_add ( block_cb , f ) ;
list_add_tail ( & block_cb - > driver_list , f - > driver_block_list ) ;
2019-05-31 09:16:57 +02:00
} else {
2019-07-09 22:55:46 +02:00
port_block = flow_block_cb_priv ( block_cb ) ;
2019-05-31 09:16:57 +02:00
}
2019-07-09 22:55:46 +02:00
flow_block_cb_incref ( block_cb ) ;
2019-05-31 09:16:57 +02:00
return 0 ;
err_cb_register :
ocelot_port_block_destroy ( port_block ) ;
return ret ;
}
2019-11-09 15:02:53 +02:00
void ocelot_setup_tc_block_flower_unbind ( struct ocelot_port_private * priv ,
2019-07-09 22:55:46 +02:00
struct flow_block_offload * f )
2019-05-31 09:16:57 +02:00
{
2019-07-09 22:55:46 +02:00
struct flow_block_cb * block_cb ;
2019-05-31 09:16:57 +02:00
2019-07-19 18:20:16 +02:00
block_cb = flow_block_cb_lookup ( f - > block ,
2019-11-09 15:02:53 +02:00
ocelot_setup_tc_block_cb_flower , priv ) ;
2019-05-31 09:16:57 +02:00
if ( ! block_cb )
return ;
2019-07-09 22:55:46 +02:00
if ( ! flow_block_cb_decref ( block_cb ) ) {
flow_block_cb_remove ( block_cb , f ) ;
list_del ( & block_cb - > driver_list ) ;
2019-05-31 09:16:57 +02:00
}
}