2019-05-28 15:49:17 +03:00
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/* Microsemi Ocelot Switch TC driver
*
* Copyright ( c ) 2019 Microsemi Corporation
*/
# include "ocelot_tc.h"
# include "ocelot_police.h"
2019-05-31 10:16:57 +03:00
# include "ocelot_ace.h"
2019-05-28 15:49:17 +03:00
# include <net/pkt_cls.h>
static int ocelot_setup_tc_cls_matchall ( struct ocelot_port * port ,
struct tc_cls_matchall_offload * f ,
bool ingress )
{
struct netlink_ext_ack * extack = f - > common . extack ;
struct ocelot_policer pol = { 0 } ;
struct flow_action_entry * action ;
int err ;
netdev_dbg ( port - > dev , " %s: port %u command %d cookie %lu \n " ,
__func__ , port - > chip_port , f - > command , f - > cookie ) ;
if ( ! ingress ) {
NL_SET_ERR_MSG_MOD ( extack , " Only ingress is supported " ) ;
return - EOPNOTSUPP ;
}
switch ( f - > command ) {
case TC_CLSMATCHALL_REPLACE :
if ( ! flow_offload_has_one_action ( & f - > rule - > action ) ) {
NL_SET_ERR_MSG_MOD ( extack ,
" Only one action is supported " ) ;
return - EOPNOTSUPP ;
}
if ( port - > tc . block_shared ) {
NL_SET_ERR_MSG_MOD ( extack ,
" Rate limit is not supported on shared blocks " ) ;
return - EOPNOTSUPP ;
}
action = & f - > rule - > action . entries [ 0 ] ;
if ( action - > id ! = FLOW_ACTION_POLICE ) {
NL_SET_ERR_MSG_MOD ( extack , " Unsupported action " ) ;
return - EOPNOTSUPP ;
}
if ( port - > tc . police_id & & port - > tc . police_id ! = f - > cookie ) {
NL_SET_ERR_MSG_MOD ( extack ,
" Only one policer per port is supported \n " ) ;
return - EEXIST ;
}
pol . rate = ( u32 ) div_u64 ( action - > police . rate_bytes_ps , 1000 ) * 8 ;
pol . burst = ( u32 ) div_u64 ( action - > police . rate_bytes_ps *
PSCHED_NS2TICKS ( action - > police . burst ) ,
PSCHED_TICKS_PER_SEC ) ;
2019-11-09 16:02:52 +03:00
err = ocelot_port_policer_add ( port - > ocelot , port - > chip_port ,
& pol ) ;
2019-05-28 15:49:17 +03:00
if ( err ) {
NL_SET_ERR_MSG_MOD ( extack , " Could not add policer \n " ) ;
return err ;
}
port - > tc . police_id = f - > cookie ;
port - > tc . offload_cnt + + ;
return 0 ;
case TC_CLSMATCHALL_DESTROY :
if ( port - > tc . police_id ! = f - > cookie )
return - ENOENT ;
2019-11-09 16:02:52 +03:00
err = ocelot_port_policer_del ( port - > ocelot , port - > chip_port ) ;
2019-05-28 15:49:17 +03:00
if ( err ) {
NL_SET_ERR_MSG_MOD ( extack ,
" Could not delete policer \n " ) ;
return err ;
}
port - > tc . police_id = 0 ;
port - > tc . offload_cnt - - ;
return 0 ;
case TC_CLSMATCHALL_STATS : /* fall through */
default :
return - EOPNOTSUPP ;
}
}
static int ocelot_setup_tc_block_cb ( enum tc_setup_type type ,
void * type_data ,
void * cb_priv , bool ingress )
{
struct ocelot_port * port = cb_priv ;
if ( ! tc_cls_can_offload_and_chain0 ( port - > dev , type_data ) )
return - EOPNOTSUPP ;
switch ( type ) {
case TC_SETUP_CLSMATCHALL :
netdev_dbg ( port - > dev , " tc_block_cb: TC_SETUP_CLSMATCHALL %s \n " ,
ingress ? " ingress " : " egress " ) ;
return ocelot_setup_tc_cls_matchall ( port , type_data , ingress ) ;
case TC_SETUP_CLSFLOWER :
2019-05-31 10:16:57 +03:00
return 0 ;
2019-05-28 15:49:17 +03:00
default :
netdev_dbg ( port - > dev , " tc_block_cb: type %d %s \n " ,
type ,
ingress ? " ingress " : " egress " ) ;
return - EOPNOTSUPP ;
}
}
static int ocelot_setup_tc_block_cb_ig ( enum tc_setup_type type ,
void * type_data ,
void * cb_priv )
{
return ocelot_setup_tc_block_cb ( type , type_data ,
cb_priv , true ) ;
}
static int ocelot_setup_tc_block_cb_eg ( enum tc_setup_type type ,
void * type_data ,
void * cb_priv )
{
return ocelot_setup_tc_block_cb ( type , type_data ,
cb_priv , false ) ;
}
2019-07-09 23:55:46 +03:00
static LIST_HEAD ( ocelot_block_cb_list ) ;
2019-05-28 15:49:17 +03:00
static int ocelot_setup_tc_block ( struct ocelot_port * port ,
2019-07-09 23:55:46 +03:00
struct flow_block_offload * f )
2019-05-28 15:49:17 +03:00
{
2019-07-09 23:55:46 +03:00
struct flow_block_cb * block_cb ;
2019-07-19 19:20:15 +03:00
flow_setup_cb_t * cb ;
2019-07-09 23:55:46 +03:00
int err ;
2019-05-28 15:49:17 +03:00
netdev_dbg ( port - > dev , " tc_block command %d, binder_type %d \n " ,
f - > command , f - > binder_type ) ;
2019-07-09 23:55:41 +03:00
if ( f - > binder_type = = FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS ) {
2019-05-28 15:49:17 +03:00
cb = ocelot_setup_tc_block_cb_ig ;
2019-07-09 23:55:46 +03:00
port - > tc . block_shared = f - > block_shared ;
2019-07-09 23:55:41 +03:00
} else if ( f - > binder_type = = FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS ) {
2019-05-28 15:49:17 +03:00
cb = ocelot_setup_tc_block_cb_eg ;
} else {
return - EOPNOTSUPP ;
}
2019-07-09 23:55:46 +03:00
f - > driver_block_list = & ocelot_block_cb_list ;
2019-05-28 15:49:17 +03:00
switch ( f - > command ) {
2019-07-09 23:55:40 +03:00
case FLOW_BLOCK_BIND :
2019-07-09 23:55:48 +03:00
if ( flow_block_cb_is_busy ( cb , port , & ocelot_block_cb_list ) )
return - EBUSY ;
2019-07-19 19:20:14 +03:00
block_cb = flow_block_cb_alloc ( cb , port , port , NULL ) ;
2019-07-09 23:55:46 +03:00
if ( IS_ERR ( block_cb ) )
return PTR_ERR ( block_cb ) ;
2019-05-31 10:16:57 +03:00
2019-07-09 23:55:46 +03:00
err = ocelot_setup_tc_block_flower_bind ( port , f ) ;
if ( err < 0 ) {
flow_block_cb_free ( block_cb ) ;
return err ;
}
flow_block_cb_add ( block_cb , f ) ;
list_add_tail ( & block_cb - > driver_list , f - > driver_block_list ) ;
return 0 ;
2019-07-09 23:55:40 +03:00
case FLOW_BLOCK_UNBIND :
2019-07-19 19:20:16 +03:00
block_cb = flow_block_cb_lookup ( f - > block , cb , port ) ;
2019-07-09 23:55:46 +03:00
if ( ! block_cb )
return - ENOENT ;
2019-05-31 10:16:57 +03:00
ocelot_setup_tc_block_flower_unbind ( port , f ) ;
2019-07-09 23:55:46 +03:00
flow_block_cb_remove ( block_cb , f ) ;
list_del ( & block_cb - > driver_list ) ;
2019-05-28 15:49:17 +03:00
return 0 ;
default :
return - EOPNOTSUPP ;
}
}
int ocelot_setup_tc ( struct net_device * dev , enum tc_setup_type type ,
void * type_data )
{
struct ocelot_port * port = netdev_priv ( dev ) ;
switch ( type ) {
case TC_SETUP_BLOCK :
return ocelot_setup_tc_block ( port , type_data ) ;
default :
return - EOPNOTSUPP ;
}
return 0 ;
}