2018-08-09 11:59:11 +03:00
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
2018-07-18 11:14:32 +03:00
# include <linux/kernel.h>
# include "core_acl_flex_actions.h"
# include "spectrum.h"
# include "spectrum_mr.h"
2018-12-10 07:11:45 +00:00
struct mlxsw_sp2_mr_tcam {
struct mlxsw_sp * mlxsw_sp ;
struct mlxsw_sp_acl_block * acl_block ;
struct mlxsw_sp_acl_ruleset * ruleset4 ;
struct mlxsw_sp_acl_ruleset * ruleset6 ;
} ;
struct mlxsw_sp2_mr_route {
struct mlxsw_sp2_mr_tcam * mr_tcam ;
} ;
static struct mlxsw_sp_acl_ruleset *
mlxsw_sp2_mr_tcam_proto_ruleset ( struct mlxsw_sp2_mr_tcam * mr_tcam ,
enum mlxsw_sp_l3proto proto )
{
switch ( proto ) {
case MLXSW_SP_L3_PROTO_IPV4 :
return mr_tcam - > ruleset4 ;
case MLXSW_SP_L3_PROTO_IPV6 :
return mr_tcam - > ruleset6 ;
}
return NULL ;
}
static int mlxsw_sp2_mr_tcam_bind_group ( struct mlxsw_sp * mlxsw_sp ,
enum mlxsw_reg_pemrbt_protocol protocol ,
struct mlxsw_sp_acl_ruleset * ruleset )
{
char pemrbt_pl [ MLXSW_REG_PEMRBT_LEN ] ;
u16 group_id ;
group_id = mlxsw_sp_acl_ruleset_group_id ( ruleset ) ;
mlxsw_reg_pemrbt_pack ( pemrbt_pl , protocol , group_id ) ;
return mlxsw_reg_write ( mlxsw_sp - > core , MLXSW_REG ( pemrbt ) , pemrbt_pl ) ;
}
static const enum mlxsw_afk_element mlxsw_sp2_mr_tcam_usage_ipv4 [ ] = {
MLXSW_AFK_ELEMENT_VIRT_ROUTER_8_10 ,
MLXSW_AFK_ELEMENT_VIRT_ROUTER_0_7 ,
MLXSW_AFK_ELEMENT_SRC_IP_0_31 ,
MLXSW_AFK_ELEMENT_DST_IP_0_31 ,
} ;
static int mlxsw_sp2_mr_tcam_ipv4_init ( struct mlxsw_sp2_mr_tcam * mr_tcam )
{
struct mlxsw_afk_element_usage elusage ;
int err ;
/* Initialize IPv4 ACL group. */
mlxsw_afk_element_usage_fill ( & elusage ,
mlxsw_sp2_mr_tcam_usage_ipv4 ,
ARRAY_SIZE ( mlxsw_sp2_mr_tcam_usage_ipv4 ) ) ;
mr_tcam - > ruleset4 = mlxsw_sp_acl_ruleset_get ( mr_tcam - > mlxsw_sp ,
mr_tcam - > acl_block ,
MLXSW_SP_L3_PROTO_IPV4 ,
MLXSW_SP_ACL_PROFILE_MR ,
& elusage ) ;
if ( IS_ERR ( mr_tcam - > ruleset4 ) )
return PTR_ERR ( mr_tcam - > ruleset4 ) ;
/* MC Router groups should be bound before routes are inserted. */
err = mlxsw_sp2_mr_tcam_bind_group ( mr_tcam - > mlxsw_sp ,
MLXSW_REG_PEMRBT_PROTO_IPV4 ,
mr_tcam - > ruleset4 ) ;
if ( err )
goto err_bind_group ;
return 0 ;
err_bind_group :
mlxsw_sp_acl_ruleset_put ( mr_tcam - > mlxsw_sp , mr_tcam - > ruleset4 ) ;
return err ;
}
static void mlxsw_sp2_mr_tcam_ipv4_fini ( struct mlxsw_sp2_mr_tcam * mr_tcam )
{
mlxsw_sp_acl_ruleset_put ( mr_tcam - > mlxsw_sp , mr_tcam - > ruleset4 ) ;
}
static const enum mlxsw_afk_element mlxsw_sp2_mr_tcam_usage_ipv6 [ ] = {
MLXSW_AFK_ELEMENT_VIRT_ROUTER_8_10 ,
MLXSW_AFK_ELEMENT_VIRT_ROUTER_0_7 ,
MLXSW_AFK_ELEMENT_SRC_IP_96_127 ,
MLXSW_AFK_ELEMENT_SRC_IP_64_95 ,
MLXSW_AFK_ELEMENT_SRC_IP_32_63 ,
MLXSW_AFK_ELEMENT_SRC_IP_0_31 ,
MLXSW_AFK_ELEMENT_DST_IP_96_127 ,
MLXSW_AFK_ELEMENT_DST_IP_64_95 ,
MLXSW_AFK_ELEMENT_DST_IP_32_63 ,
MLXSW_AFK_ELEMENT_DST_IP_0_31 ,
} ;
static int mlxsw_sp2_mr_tcam_ipv6_init ( struct mlxsw_sp2_mr_tcam * mr_tcam )
{
struct mlxsw_afk_element_usage elusage ;
int err ;
/* Initialize IPv6 ACL group */
mlxsw_afk_element_usage_fill ( & elusage ,
mlxsw_sp2_mr_tcam_usage_ipv6 ,
ARRAY_SIZE ( mlxsw_sp2_mr_tcam_usage_ipv6 ) ) ;
mr_tcam - > ruleset6 = mlxsw_sp_acl_ruleset_get ( mr_tcam - > mlxsw_sp ,
mr_tcam - > acl_block ,
MLXSW_SP_L3_PROTO_IPV6 ,
MLXSW_SP_ACL_PROFILE_MR ,
& elusage ) ;
if ( IS_ERR ( mr_tcam - > ruleset6 ) )
return PTR_ERR ( mr_tcam - > ruleset6 ) ;
/* MC Router groups should be bound before routes are inserted. */
err = mlxsw_sp2_mr_tcam_bind_group ( mr_tcam - > mlxsw_sp ,
MLXSW_REG_PEMRBT_PROTO_IPV6 ,
mr_tcam - > ruleset6 ) ;
if ( err )
goto err_bind_group ;
return 0 ;
err_bind_group :
mlxsw_sp_acl_ruleset_put ( mr_tcam - > mlxsw_sp , mr_tcam - > ruleset6 ) ;
return err ;
}
static void mlxsw_sp2_mr_tcam_ipv6_fini ( struct mlxsw_sp2_mr_tcam * mr_tcam )
{
mlxsw_sp_acl_ruleset_put ( mr_tcam - > mlxsw_sp , mr_tcam - > ruleset6 ) ;
}
static void
mlxsw_sp2_mr_tcam_rule_parse4 ( struct mlxsw_sp_acl_rule_info * rulei ,
struct mlxsw_sp_mr_route_key * key )
{
mlxsw_sp_acl_rulei_keymask_buf ( rulei , MLXSW_AFK_ELEMENT_SRC_IP_0_31 ,
( char * ) & key - > source . addr4 ,
( char * ) & key - > source_mask . addr4 , 4 ) ;
mlxsw_sp_acl_rulei_keymask_buf ( rulei , MLXSW_AFK_ELEMENT_DST_IP_0_31 ,
( char * ) & key - > group . addr4 ,
( char * ) & key - > group_mask . addr4 , 4 ) ;
}
static void
mlxsw_sp2_mr_tcam_rule_parse6 ( struct mlxsw_sp_acl_rule_info * rulei ,
struct mlxsw_sp_mr_route_key * key )
{
mlxsw_sp_acl_rulei_keymask_buf ( rulei , MLXSW_AFK_ELEMENT_SRC_IP_96_127 ,
& key - > source . addr6 . s6_addr [ 0x0 ] ,
& key - > source_mask . addr6 . s6_addr [ 0x0 ] , 4 ) ;
mlxsw_sp_acl_rulei_keymask_buf ( rulei , MLXSW_AFK_ELEMENT_SRC_IP_64_95 ,
& key - > source . addr6 . s6_addr [ 0x4 ] ,
& key - > source_mask . addr6 . s6_addr [ 0x4 ] , 4 ) ;
mlxsw_sp_acl_rulei_keymask_buf ( rulei , MLXSW_AFK_ELEMENT_SRC_IP_32_63 ,
& key - > source . addr6 . s6_addr [ 0x8 ] ,
& key - > source_mask . addr6 . s6_addr [ 0x8 ] , 4 ) ;
mlxsw_sp_acl_rulei_keymask_buf ( rulei , MLXSW_AFK_ELEMENT_SRC_IP_0_31 ,
& key - > source . addr6 . s6_addr [ 0xc ] ,
& key - > source_mask . addr6 . s6_addr [ 0xc ] , 4 ) ;
mlxsw_sp_acl_rulei_keymask_buf ( rulei , MLXSW_AFK_ELEMENT_DST_IP_96_127 ,
& key - > group . addr6 . s6_addr [ 0x0 ] ,
& key - > group_mask . addr6 . s6_addr [ 0x0 ] , 4 ) ;
mlxsw_sp_acl_rulei_keymask_buf ( rulei , MLXSW_AFK_ELEMENT_DST_IP_64_95 ,
& key - > group . addr6 . s6_addr [ 0x4 ] ,
& key - > group_mask . addr6 . s6_addr [ 0x4 ] , 4 ) ;
mlxsw_sp_acl_rulei_keymask_buf ( rulei , MLXSW_AFK_ELEMENT_DST_IP_32_63 ,
& key - > group . addr6 . s6_addr [ 0x8 ] ,
& key - > group_mask . addr6 . s6_addr [ 0x8 ] , 4 ) ;
mlxsw_sp_acl_rulei_keymask_buf ( rulei , MLXSW_AFK_ELEMENT_DST_IP_0_31 ,
& key - > group . addr6 . s6_addr [ 0xc ] ,
& key - > group_mask . addr6 . s6_addr [ 0xc ] , 4 ) ;
}
static void
mlxsw_sp2_mr_tcam_rule_parse ( struct mlxsw_sp_acl_rule * rule ,
struct mlxsw_sp_mr_route_key * key ,
unsigned int priority )
{
struct mlxsw_sp_acl_rule_info * rulei ;
rulei = mlxsw_sp_acl_rule_rulei ( rule ) ;
rulei - > priority = priority ;
mlxsw_sp_acl_rulei_keymask_u32 ( rulei , MLXSW_AFK_ELEMENT_VIRT_ROUTER_0_7 ,
key - > vrid , GENMASK ( 7 , 0 ) ) ;
mlxsw_sp_acl_rulei_keymask_u32 ( rulei ,
MLXSW_AFK_ELEMENT_VIRT_ROUTER_8_10 ,
key - > vrid > > 8 , GENMASK ( 2 , 0 ) ) ;
switch ( key - > proto ) {
case MLXSW_SP_L3_PROTO_IPV4 :
return mlxsw_sp2_mr_tcam_rule_parse4 ( rulei , key ) ;
case MLXSW_SP_L3_PROTO_IPV6 :
return mlxsw_sp2_mr_tcam_rule_parse6 ( rulei , key ) ;
}
}
2018-07-18 11:14:32 +03:00
static int
mlxsw_sp2_mr_tcam_route_create ( struct mlxsw_sp * mlxsw_sp , void * priv ,
void * route_priv ,
struct mlxsw_sp_mr_route_key * key ,
struct mlxsw_afa_block * afa_block ,
enum mlxsw_sp_mr_route_prio prio )
{
2018-12-10 07:11:45 +00:00
struct mlxsw_sp2_mr_route * mr_route = route_priv ;
struct mlxsw_sp2_mr_tcam * mr_tcam = priv ;
struct mlxsw_sp_acl_ruleset * ruleset ;
struct mlxsw_sp_acl_rule * rule ;
int err ;
mr_route - > mr_tcam = mr_tcam ;
ruleset = mlxsw_sp2_mr_tcam_proto_ruleset ( mr_tcam , key - > proto ) ;
if ( WARN_ON ( ! ruleset ) )
return - EINVAL ;
rule = mlxsw_sp_acl_rule_create ( mlxsw_sp , ruleset ,
( unsigned long ) route_priv , afa_block ,
NULL ) ;
if ( IS_ERR ( rule ) )
return PTR_ERR ( rule ) ;
mlxsw_sp2_mr_tcam_rule_parse ( rule , key , prio ) ;
err = mlxsw_sp_acl_rule_add ( mlxsw_sp , rule ) ;
if ( err )
goto err_rule_add ;
2018-07-18 11:14:32 +03:00
return 0 ;
2018-12-10 07:11:45 +00:00
err_rule_add :
mlxsw_sp_acl_rule_destroy ( mlxsw_sp , rule ) ;
return err ;
2018-07-18 11:14:32 +03:00
}
static void
mlxsw_sp2_mr_tcam_route_destroy ( struct mlxsw_sp * mlxsw_sp , void * priv ,
void * route_priv ,
struct mlxsw_sp_mr_route_key * key )
{
2018-12-10 07:11:45 +00:00
struct mlxsw_sp2_mr_tcam * mr_tcam = priv ;
struct mlxsw_sp_acl_ruleset * ruleset ;
struct mlxsw_sp_acl_rule * rule ;
ruleset = mlxsw_sp2_mr_tcam_proto_ruleset ( mr_tcam , key - > proto ) ;
if ( WARN_ON ( ! ruleset ) )
return ;
rule = mlxsw_sp_acl_rule_lookup ( mlxsw_sp , ruleset ,
( unsigned long ) route_priv ) ;
if ( WARN_ON ( ! rule ) )
return ;
mlxsw_sp_acl_rule_del ( mlxsw_sp , rule ) ;
mlxsw_sp_acl_rule_destroy ( mlxsw_sp , rule ) ;
2018-07-18 11:14:32 +03:00
}
static int
mlxsw_sp2_mr_tcam_route_update ( struct mlxsw_sp * mlxsw_sp ,
void * route_priv ,
struct mlxsw_sp_mr_route_key * key ,
struct mlxsw_afa_block * afa_block )
{
2018-12-10 07:11:45 +00:00
struct mlxsw_sp2_mr_route * mr_route = route_priv ;
struct mlxsw_sp2_mr_tcam * mr_tcam = mr_route - > mr_tcam ;
struct mlxsw_sp_acl_ruleset * ruleset ;
struct mlxsw_sp_acl_rule * rule ;
ruleset = mlxsw_sp2_mr_tcam_proto_ruleset ( mr_tcam , key - > proto ) ;
if ( WARN_ON ( ! ruleset ) )
return - EINVAL ;
rule = mlxsw_sp_acl_rule_lookup ( mlxsw_sp , ruleset ,
( unsigned long ) route_priv ) ;
if ( WARN_ON ( ! rule ) )
return - EINVAL ;
return mlxsw_sp_acl_rule_action_replace ( mlxsw_sp , rule , afa_block ) ;
2018-07-18 11:14:32 +03:00
}
static int mlxsw_sp2_mr_tcam_init ( struct mlxsw_sp * mlxsw_sp , void * priv )
{
2018-12-10 07:11:45 +00:00
struct mlxsw_sp2_mr_tcam * mr_tcam = priv ;
int err ;
mr_tcam - > mlxsw_sp = mlxsw_sp ;
mr_tcam - > acl_block = mlxsw_sp_acl_block_create ( mlxsw_sp , NULL ) ;
if ( ! mr_tcam - > acl_block )
return - ENOMEM ;
err = mlxsw_sp2_mr_tcam_ipv4_init ( mr_tcam ) ;
if ( err )
goto err_ipv4_init ;
err = mlxsw_sp2_mr_tcam_ipv6_init ( mr_tcam ) ;
if ( err )
goto err_ipv6_init ;
2018-07-18 11:14:32 +03:00
return 0 ;
2018-12-10 07:11:45 +00:00
err_ipv6_init :
mlxsw_sp2_mr_tcam_ipv4_fini ( mr_tcam ) ;
err_ipv4_init :
mlxsw_sp_acl_block_destroy ( mr_tcam - > acl_block ) ;
return err ;
2018-07-18 11:14:32 +03:00
}
static void mlxsw_sp2_mr_tcam_fini ( void * priv )
{
2018-12-10 07:11:45 +00:00
struct mlxsw_sp2_mr_tcam * mr_tcam = priv ;
mlxsw_sp2_mr_tcam_ipv6_fini ( mr_tcam ) ;
mlxsw_sp2_mr_tcam_ipv4_fini ( mr_tcam ) ;
mlxsw_sp_acl_block_destroy ( mr_tcam - > acl_block ) ;
2018-07-18 11:14:32 +03:00
}
const struct mlxsw_sp_mr_tcam_ops mlxsw_sp2_mr_tcam_ops = {
2018-12-10 07:11:45 +00:00
. priv_size = sizeof ( struct mlxsw_sp2_mr_tcam ) ,
2018-07-18 11:14:32 +03:00
. init = mlxsw_sp2_mr_tcam_init ,
. fini = mlxsw_sp2_mr_tcam_fini ,
2018-12-10 07:11:45 +00:00
. route_priv_size = sizeof ( struct mlxsw_sp2_mr_route ) ,
2018-07-18 11:14:32 +03:00
. route_create = mlxsw_sp2_mr_tcam_route_create ,
. route_destroy = mlxsw_sp2_mr_tcam_route_destroy ,
. route_update = mlxsw_sp2_mr_tcam_route_update ,
} ;