2021-06-16 19:01:44 +03:00
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2021-11-30 12:32:58 +02:00
/* Copyright (c) 2020-2021 Marvell International Ltd. All rights reserved */
2021-06-16 19:01:44 +03:00
# include <linux/rhashtable.h>
# include "prestera_acl.h"
2021-11-30 12:32:58 +02:00
# include "prestera_flow.h"
# include "prestera_hw.h"
# include "prestera.h"
# define ACL_KEYMASK_SIZE \
( sizeof ( __be32 ) * __PRESTERA_ACL_RULE_MATCH_TYPE_MAX )
2021-06-16 19:01:44 +03:00
struct prestera_acl {
struct prestera_switch * sw ;
2021-11-30 12:32:58 +02:00
struct list_head vtcam_list ;
2021-06-16 19:01:44 +03:00
struct list_head rules ;
2021-11-30 12:32:58 +02:00
struct rhashtable ruleset_ht ;
struct rhashtable acl_rule_entry_ht ;
struct idr uid ;
} ;
struct prestera_acl_ruleset_ht_key {
struct prestera_flow_block * block ;
2022-02-14 10:20:06 +02:00
u32 chain_index ;
2021-11-30 12:32:58 +02:00
} ;
struct prestera_acl_rule_entry {
struct rhash_head ht_node ;
struct prestera_acl_rule_entry_key key ;
u32 hw_id ;
u32 vtcam_id ;
struct {
struct {
u8 valid : 1 ;
} accept , drop , trap ;
2022-04-27 15:05:48 +03:00
struct {
u8 valid : 1 ;
struct prestera_acl_action_police i ;
} police ;
2022-02-14 10:20:06 +02:00
struct {
struct prestera_acl_action_jump i ;
u8 valid : 1 ;
} jump ;
2021-11-30 12:33:00 +02:00
struct {
u32 id ;
struct prestera_counter_block * block ;
} counter ;
2021-11-30 12:32:58 +02:00
} ;
2021-06-16 19:01:44 +03:00
} ;
struct prestera_acl_ruleset {
2021-11-30 12:32:58 +02:00
struct rhash_head ht_node ; /* Member of acl HT */
struct prestera_acl_ruleset_ht_key ht_key ;
2021-06-16 19:01:44 +03:00
struct rhashtable rule_ht ;
2021-11-30 12:32:58 +02:00
struct prestera_acl * acl ;
2022-08-23 14:39:58 +03:00
struct {
u32 min ;
u32 max ;
} prio ;
2021-11-30 12:32:58 +02:00
unsigned long rule_count ;
refcount_t refcount ;
void * keymask ;
u32 vtcam_id ;
2022-02-14 10:20:06 +02:00
u32 index ;
2021-11-30 12:32:58 +02:00
u16 pcl_id ;
bool offload ;
2022-06-27 12:50:18 +03:00
bool ingress ;
2021-06-16 19:01:44 +03:00
} ;
2021-11-30 12:32:58 +02:00
struct prestera_acl_vtcam {
2021-06-16 19:01:44 +03:00
struct list_head list ;
2021-11-30 12:32:58 +02:00
__be32 keymask [ __PRESTERA_ACL_RULE_MATCH_TYPE_MAX ] ;
refcount_t refcount ;
2021-06-16 19:01:44 +03:00
u32 id ;
2021-11-30 12:32:58 +02:00
bool is_keymask_set ;
u8 lookup ;
2022-06-27 12:50:18 +03:00
u8 direction ;
2021-11-30 12:32:58 +02:00
} ;
static const struct rhashtable_params prestera_acl_ruleset_ht_params = {
. key_len = sizeof ( struct prestera_acl_ruleset_ht_key ) ,
. key_offset = offsetof ( struct prestera_acl_ruleset , ht_key ) ,
. head_offset = offsetof ( struct prestera_acl_ruleset , ht_node ) ,
. automatic_shrinking = true ,
2021-06-16 19:01:44 +03:00
} ;
static const struct rhashtable_params prestera_acl_rule_ht_params = {
. key_len = sizeof ( unsigned long ) ,
. key_offset = offsetof ( struct prestera_acl_rule , cookie ) ,
. head_offset = offsetof ( struct prestera_acl_rule , ht_node ) ,
. automatic_shrinking = true ,
} ;
2021-11-30 12:32:58 +02:00
static const struct rhashtable_params __prestera_acl_rule_entry_ht_params = {
. key_offset = offsetof ( struct prestera_acl_rule_entry , key ) ,
. head_offset = offsetof ( struct prestera_acl_rule_entry , ht_node ) ,
. key_len = sizeof ( struct prestera_acl_rule_entry_key ) ,
. automatic_shrinking = true ,
} ;
2022-06-27 12:50:18 +03:00
int prestera_acl_chain_to_client ( u32 chain_index , bool ingress , u32 * client )
2022-02-14 10:20:06 +02:00
{
2022-06-27 12:50:18 +03:00
static const u32 ingress_client_map [ ] = {
PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_0 ,
PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_1 ,
PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_2
2022-02-14 10:20:06 +02:00
} ;
2022-06-27 12:50:18 +03:00
if ( ! ingress ) {
/* prestera supports only one chain on egress */
if ( chain_index > 0 )
return - EINVAL ;
* client = PRESTERA_HW_COUNTER_CLIENT_EGRESS_LOOKUP ;
return 0 ;
}
if ( chain_index > = ARRAY_SIZE ( ingress_client_map ) )
2022-02-14 10:20:06 +02:00
return - EINVAL ;
2022-06-27 12:50:18 +03:00
* client = ingress_client_map [ chain_index ] ;
2022-02-14 10:20:06 +02:00
return 0 ;
}
2022-06-27 12:50:18 +03:00
static bool prestera_acl_chain_is_supported ( u32 chain_index , bool ingress )
2022-02-14 10:20:06 +02:00
{
2022-06-27 12:50:18 +03:00
if ( ! ingress )
/* prestera supports only one chain on egress */
return chain_index = = 0 ;
2022-02-14 10:20:06 +02:00
return ( chain_index & ~ PRESTERA_ACL_CHAIN_MASK ) = = 0 ;
}
2021-06-16 19:01:44 +03:00
static struct prestera_acl_ruleset *
2021-11-30 12:32:58 +02:00
prestera_acl_ruleset_create ( struct prestera_acl * acl ,
2022-02-14 10:20:06 +02:00
struct prestera_flow_block * block ,
u32 chain_index )
2021-06-16 19:01:44 +03:00
{
struct prestera_acl_ruleset * ruleset ;
2021-12-15 12:07:30 +02:00
u32 uid = 0 ;
2021-06-16 19:01:44 +03:00
int err ;
2022-06-27 12:50:18 +03:00
if ( ! prestera_acl_chain_is_supported ( chain_index , block - > ingress ) )
2022-02-14 10:20:06 +02:00
return ERR_PTR ( - EINVAL ) ;
2021-06-16 19:01:44 +03:00
ruleset = kzalloc ( sizeof ( * ruleset ) , GFP_KERNEL ) ;
if ( ! ruleset )
return ERR_PTR ( - ENOMEM ) ;
2021-11-30 12:32:58 +02:00
ruleset - > acl = acl ;
2022-06-27 12:50:18 +03:00
ruleset - > ingress = block - > ingress ;
2021-11-30 12:32:58 +02:00
ruleset - > ht_key . block = block ;
2022-02-14 10:20:06 +02:00
ruleset - > ht_key . chain_index = chain_index ;
2021-11-30 12:32:58 +02:00
refcount_set ( & ruleset - > refcount , 1 ) ;
2021-06-16 19:01:44 +03:00
err = rhashtable_init ( & ruleset - > rule_ht , & prestera_acl_rule_ht_params ) ;
if ( err )
goto err_rhashtable_init ;
2021-11-30 12:32:58 +02:00
err = idr_alloc_u32 ( & acl - > uid , NULL , & uid , U8_MAX , GFP_KERNEL ) ;
2021-06-16 19:01:44 +03:00
if ( err )
goto err_ruleset_create ;
2021-11-30 12:32:58 +02:00
/* make pcl-id based on uid */
2022-02-14 10:20:06 +02:00
ruleset - > pcl_id = PRESTERA_ACL_PCL_ID_MAKE ( ( u8 ) uid , chain_index ) ;
ruleset - > index = uid ;
2022-08-23 14:39:58 +03:00
ruleset - > prio . min = UINT_MAX ;
ruleset - > prio . max = 0 ;
2021-11-30 12:32:58 +02:00
err = rhashtable_insert_fast ( & acl - > ruleset_ht , & ruleset - > ht_node ,
prestera_acl_ruleset_ht_params ) ;
if ( err )
goto err_ruleset_ht_insert ;
2021-06-16 19:01:44 +03:00
return ruleset ;
2021-11-30 12:32:58 +02:00
err_ruleset_ht_insert :
idr_remove ( & acl - > uid , uid ) ;
2021-06-16 19:01:44 +03:00
err_ruleset_create :
rhashtable_destroy ( & ruleset - > rule_ht ) ;
err_rhashtable_init :
kfree ( ruleset ) ;
return ERR_PTR ( err ) ;
}
2022-09-30 12:48:43 +08:00
int prestera_acl_ruleset_keymask_set ( struct prestera_acl_ruleset * ruleset ,
void * keymask )
2021-12-15 12:07:30 +02:00
{
ruleset - > keymask = kmemdup ( keymask , ACL_KEYMASK_SIZE , GFP_KERNEL ) ;
2022-09-30 12:48:43 +08:00
if ( ! ruleset - > keymask )
return - ENOMEM ;
return 0 ;
2021-12-15 12:07:30 +02:00
}
2021-11-30 12:32:58 +02:00
int prestera_acl_ruleset_offload ( struct prestera_acl_ruleset * ruleset )
{
2022-02-14 10:20:06 +02:00
struct prestera_acl_iface iface ;
2021-11-30 12:32:58 +02:00
u32 vtcam_id ;
2022-06-27 12:50:18 +03:00
int dir ;
2021-11-30 12:32:58 +02:00
int err ;
2022-06-27 12:50:18 +03:00
dir = ruleset - > ingress ?
PRESTERA_HW_VTCAM_DIR_INGRESS : PRESTERA_HW_VTCAM_DIR_EGRESS ;
2021-11-30 12:32:58 +02:00
if ( ruleset - > offload )
return - EEXIST ;
2022-02-14 10:20:06 +02:00
err = prestera_acl_vtcam_id_get ( ruleset - > acl ,
ruleset - > ht_key . chain_index ,
2022-06-27 12:50:18 +03:00
dir ,
2021-11-30 12:32:58 +02:00
ruleset - > keymask , & vtcam_id ) ;
if ( err )
2022-02-14 10:20:06 +02:00
goto err_vtcam_create ;
if ( ruleset - > ht_key . chain_index ) {
/* for chain > 0, bind iface index to pcl-id to be able
* to jump from any other ruleset to this one using the index .
*/
iface . index = ruleset - > index ;
iface . type = PRESTERA_ACL_IFACE_TYPE_INDEX ;
err = prestera_hw_vtcam_iface_bind ( ruleset - > acl - > sw , & iface ,
vtcam_id , ruleset - > pcl_id ) ;
if ( err )
goto err_ruleset_bind ;
}
2021-11-30 12:32:58 +02:00
ruleset - > vtcam_id = vtcam_id ;
ruleset - > offload = true ;
return 0 ;
2022-02-14 10:20:06 +02:00
err_ruleset_bind :
prestera_acl_vtcam_id_put ( ruleset - > acl , ruleset - > vtcam_id ) ;
err_vtcam_create :
return err ;
2021-11-30 12:32:58 +02:00
}
2021-06-16 19:01:44 +03:00
static void prestera_acl_ruleset_destroy ( struct prestera_acl_ruleset * ruleset )
{
2021-11-30 12:32:58 +02:00
struct prestera_acl * acl = ruleset - > acl ;
u8 uid = ruleset - > pcl_id & PRESTERA_ACL_KEYMASK_PCL_ID_USER ;
2022-02-14 10:20:06 +02:00
int err ;
2021-11-30 12:32:58 +02:00
rhashtable_remove_fast ( & acl - > ruleset_ht , & ruleset - > ht_node ,
prestera_acl_ruleset_ht_params ) ;
2022-02-14 10:20:06 +02:00
if ( ruleset - > offload ) {
if ( ruleset - > ht_key . chain_index ) {
struct prestera_acl_iface iface = {
. type = PRESTERA_ACL_IFACE_TYPE_INDEX ,
. index = ruleset - > index
} ;
err = prestera_hw_vtcam_iface_unbind ( acl - > sw , & iface ,
ruleset - > vtcam_id ) ;
WARN_ON ( err ) ;
}
2021-11-30 12:32:58 +02:00
WARN_ON ( prestera_acl_vtcam_id_put ( acl , ruleset - > vtcam_id ) ) ;
2022-02-14 10:20:06 +02:00
}
2021-11-30 12:32:58 +02:00
idr_remove ( & acl - > uid , uid ) ;
2021-06-16 19:01:44 +03:00
rhashtable_destroy ( & ruleset - > rule_ht ) ;
2021-11-30 12:32:58 +02:00
kfree ( ruleset - > keymask ) ;
2021-06-16 19:01:44 +03:00
kfree ( ruleset ) ;
}
2021-11-30 12:32:58 +02:00
static struct prestera_acl_ruleset *
__prestera_acl_ruleset_lookup ( struct prestera_acl * acl ,
2022-02-14 10:20:06 +02:00
struct prestera_flow_block * block ,
u32 chain_index )
2021-06-16 19:01:44 +03:00
{
2021-11-30 12:32:58 +02:00
struct prestera_acl_ruleset_ht_key ht_key ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
memset ( & ht_key , 0 , sizeof ( ht_key ) ) ;
ht_key . block = block ;
2022-02-14 10:20:06 +02:00
ht_key . chain_index = chain_index ;
2021-11-30 12:32:58 +02:00
return rhashtable_lookup_fast ( & acl - > ruleset_ht , & ht_key ,
prestera_acl_ruleset_ht_params ) ;
2021-06-16 19:01:44 +03:00
}
2021-11-30 12:32:58 +02:00
struct prestera_acl_ruleset *
prestera_acl_ruleset_lookup ( struct prestera_acl * acl ,
2022-02-14 10:20:06 +02:00
struct prestera_flow_block * block ,
u32 chain_index )
2021-06-16 19:01:44 +03:00
{
2021-11-30 12:32:58 +02:00
struct prestera_acl_ruleset * ruleset ;
2022-02-14 10:20:06 +02:00
ruleset = __prestera_acl_ruleset_lookup ( acl , block , chain_index ) ;
2021-11-30 12:32:58 +02:00
if ( ! ruleset )
return ERR_PTR ( - ENOENT ) ;
refcount_inc ( & ruleset - > refcount ) ;
return ruleset ;
2021-06-16 19:01:44 +03:00
}
2021-11-30 12:32:58 +02:00
struct prestera_acl_ruleset *
prestera_acl_ruleset_get ( struct prestera_acl * acl ,
2022-02-14 10:20:06 +02:00
struct prestera_flow_block * block ,
u32 chain_index )
2021-06-16 19:01:44 +03:00
{
2021-11-30 12:32:58 +02:00
struct prestera_acl_ruleset * ruleset ;
2021-06-16 19:01:44 +03:00
2022-02-14 10:20:06 +02:00
ruleset = __prestera_acl_ruleset_lookup ( acl , block , chain_index ) ;
2021-11-30 12:32:58 +02:00
if ( ruleset ) {
refcount_inc ( & ruleset - > refcount ) ;
return ruleset ;
}
2021-06-16 19:01:44 +03:00
2022-02-14 10:20:06 +02:00
return prestera_acl_ruleset_create ( acl , block , chain_index ) ;
2021-06-16 19:01:44 +03:00
}
2021-11-30 12:32:58 +02:00
void prestera_acl_ruleset_put ( struct prestera_acl_ruleset * ruleset )
2021-06-16 19:01:44 +03:00
{
2021-11-30 12:32:58 +02:00
if ( ! refcount_dec_and_test ( & ruleset - > refcount ) )
return ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
prestera_acl_ruleset_destroy ( ruleset ) ;
}
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
int prestera_acl_ruleset_bind ( struct prestera_acl_ruleset * ruleset ,
struct prestera_port * port )
{
struct prestera_acl_iface iface = {
. type = PRESTERA_ACL_IFACE_TYPE_PORT ,
. port = port
} ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
return prestera_hw_vtcam_iface_bind ( port - > sw , & iface , ruleset - > vtcam_id ,
ruleset - > pcl_id ) ;
}
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
int prestera_acl_ruleset_unbind ( struct prestera_acl_ruleset * ruleset ,
struct prestera_port * port )
{
struct prestera_acl_iface iface = {
. type = PRESTERA_ACL_IFACE_TYPE_PORT ,
. port = port
} ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
return prestera_hw_vtcam_iface_unbind ( port - > sw , & iface ,
ruleset - > vtcam_id ) ;
2021-06-16 19:01:44 +03:00
}
2021-11-30 12:32:58 +02:00
static int prestera_acl_ruleset_block_bind ( struct prestera_acl_ruleset * ruleset ,
struct prestera_flow_block * block )
2021-06-16 19:01:44 +03:00
{
struct prestera_flow_block_binding * binding ;
2021-11-30 12:32:58 +02:00
int err ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
block - > ruleset_zero = ruleset ;
list_for_each_entry ( binding , & block - > binding_list , list ) {
err = prestera_acl_ruleset_bind ( ruleset , binding - > port ) ;
if ( err )
goto rollback ;
}
return 0 ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
rollback :
list_for_each_entry_continue_reverse ( binding , & block - > binding_list ,
list )
err = prestera_acl_ruleset_unbind ( ruleset , binding - > port ) ;
block - > ruleset_zero = NULL ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
return err ;
2021-06-16 19:01:44 +03:00
}
2021-11-30 12:32:58 +02:00
static void
prestera_acl_ruleset_block_unbind ( struct prestera_acl_ruleset * ruleset ,
struct prestera_flow_block * block )
2021-06-16 19:01:44 +03:00
{
2021-11-30 12:32:58 +02:00
struct prestera_flow_block_binding * binding ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
list_for_each_entry ( binding , & block - > binding_list , list )
prestera_acl_ruleset_unbind ( ruleset , binding - > port ) ;
block - > ruleset_zero = NULL ;
2021-06-16 19:01:44 +03:00
}
2022-08-23 14:39:58 +03:00
static void
prestera_acl_ruleset_prio_refresh ( struct prestera_acl * acl ,
struct prestera_acl_ruleset * ruleset )
{
struct prestera_acl_rule * rule ;
ruleset - > prio . min = UINT_MAX ;
ruleset - > prio . max = 0 ;
list_for_each_entry ( rule , & acl - > rules , list ) {
if ( ruleset - > ingress ! = rule - > ruleset - > ingress )
continue ;
if ( ruleset - > ht_key . chain_index ! = rule - > chain_index )
continue ;
ruleset - > prio . min = min ( ruleset - > prio . min , rule - > priority ) ;
ruleset - > prio . max = max ( ruleset - > prio . max , rule - > priority ) ;
}
}
2021-11-30 12:32:58 +02:00
void
prestera_acl_rule_keymask_pcl_id_set ( struct prestera_acl_rule * rule , u16 pcl_id )
2021-06-16 19:01:44 +03:00
{
2021-11-30 12:32:58 +02:00
struct prestera_acl_match * r_match = & rule - > re_key . match ;
__be16 pcl_id_mask = htons ( PRESTERA_ACL_KEYMASK_PCL_ID ) ;
__be16 pcl_id_key = htons ( pcl_id ) ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
rule_match_set ( r_match - > key , PCL_ID , pcl_id_key ) ;
rule_match_set ( r_match - > mask , PCL_ID , pcl_id_mask ) ;
2021-06-16 19:01:44 +03:00
}
struct prestera_acl_rule *
prestera_acl_rule_lookup ( struct prestera_acl_ruleset * ruleset ,
unsigned long cookie )
{
return rhashtable_lookup_fast ( & ruleset - > rule_ht , & cookie ,
prestera_acl_rule_ht_params ) ;
}
2022-02-14 10:20:06 +02:00
u32 prestera_acl_ruleset_index_get ( const struct prestera_acl_ruleset * ruleset )
{
return ruleset - > index ;
}
2022-08-23 14:39:58 +03:00
void prestera_acl_ruleset_prio_get ( struct prestera_acl_ruleset * ruleset ,
u32 * prio_min , u32 * prio_max )
{
* prio_min = ruleset - > prio . min ;
* prio_max = ruleset - > prio . max ;
}
2021-11-30 12:32:58 +02:00
bool prestera_acl_ruleset_is_offload ( struct prestera_acl_ruleset * ruleset )
{
return ruleset - > offload ;
}
2021-06-16 19:01:44 +03:00
struct prestera_acl_rule *
2021-11-30 12:32:58 +02:00
prestera_acl_rule_create ( struct prestera_acl_ruleset * ruleset ,
2022-02-14 10:20:06 +02:00
unsigned long cookie , u32 chain_index )
2021-06-16 19:01:44 +03:00
{
struct prestera_acl_rule * rule ;
rule = kzalloc ( sizeof ( * rule ) , GFP_KERNEL ) ;
if ( ! rule )
return ERR_PTR ( - ENOMEM ) ;
2021-11-30 12:32:58 +02:00
rule - > ruleset = ruleset ;
2021-06-16 19:01:44 +03:00
rule - > cookie = cookie ;
2022-02-14 10:20:06 +02:00
rule - > chain_index = chain_index ;
2021-11-30 12:32:58 +02:00
refcount_inc ( & ruleset - > refcount ) ;
2021-06-16 19:01:44 +03:00
return rule ;
}
2021-11-30 12:32:58 +02:00
void prestera_acl_rule_priority_set ( struct prestera_acl_rule * rule ,
u32 priority )
2021-06-16 19:01:44 +03:00
{
2021-11-30 12:32:58 +02:00
rule - > priority = priority ;
2021-06-16 19:01:44 +03:00
}
2021-11-30 12:32:58 +02:00
void prestera_acl_rule_destroy ( struct prestera_acl_rule * rule )
2021-06-16 19:01:44 +03:00
{
2022-02-14 10:20:06 +02:00
if ( rule - > jump_ruleset )
/* release ruleset kept by jump action */
prestera_acl_ruleset_put ( rule - > jump_ruleset ) ;
2021-11-30 12:32:58 +02:00
prestera_acl_ruleset_put ( rule - > ruleset ) ;
kfree ( rule ) ;
2021-06-16 19:01:44 +03:00
}
2022-08-23 14:39:58 +03:00
static void prestera_acl_ruleset_prio_update ( struct prestera_acl_ruleset * ruleset ,
u32 prio )
{
ruleset - > prio . min = min ( ruleset - > prio . min , prio ) ;
ruleset - > prio . max = max ( ruleset - > prio . max , prio ) ;
}
2021-11-30 12:32:58 +02:00
int prestera_acl_rule_add ( struct prestera_switch * sw ,
struct prestera_acl_rule * rule )
2021-06-16 19:01:44 +03:00
{
2021-11-30 12:32:58 +02:00
int err ;
struct prestera_acl_ruleset * ruleset = rule - > ruleset ;
struct prestera_flow_block * block = ruleset - > ht_key . block ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
/* try to add rule to hash table first */
err = rhashtable_insert_fast ( & ruleset - > rule_ht , & rule - > ht_node ,
prestera_acl_rule_ht_params ) ;
if ( err )
goto err_ht_insert ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
prestera_acl_rule_keymask_pcl_id_set ( rule , ruleset - > pcl_id ) ;
rule - > re_arg . vtcam_id = ruleset - > vtcam_id ;
rule - > re_key . prio = rule - > priority ;
rule - > re = prestera_acl_rule_entry_find ( sw - > acl , & rule - > re_key ) ;
err = WARN_ON ( rule - > re ) ? - EEXIST : 0 ;
if ( err )
goto err_rule_add ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
rule - > re = prestera_acl_rule_entry_create ( sw - > acl , & rule - > re_key ,
& rule - > re_arg ) ;
err = ! rule - > re ? - EINVAL : 0 ;
if ( err )
goto err_rule_add ;
2022-02-14 10:20:06 +02:00
/* bind the block (all ports) to chain index 0, rest of
* the chains are bound to goto action
*/
if ( ! ruleset - > ht_key . chain_index & & ! ruleset - > rule_count ) {
2021-11-30 12:32:58 +02:00
err = prestera_acl_ruleset_block_bind ( ruleset , block ) ;
if ( err )
goto err_acl_block_bind ;
}
list_add_tail ( & rule - > list , & sw - > acl - > rules ) ;
ruleset - > rule_count + + ;
2022-08-23 14:39:58 +03:00
prestera_acl_ruleset_prio_update ( ruleset , rule - > priority ) ;
2021-06-16 19:01:44 +03:00
return 0 ;
2021-11-30 12:32:58 +02:00
err_acl_block_bind :
prestera_acl_rule_entry_destroy ( sw - > acl , rule - > re ) ;
err_rule_add :
rule - > re = NULL ;
rhashtable_remove_fast ( & ruleset - > rule_ht , & rule - > ht_node ,
prestera_acl_rule_ht_params ) ;
err_ht_insert :
return err ;
2021-06-16 19:01:44 +03:00
}
2021-11-30 12:32:58 +02:00
void prestera_acl_rule_del ( struct prestera_switch * sw ,
struct prestera_acl_rule * rule )
2021-06-16 19:01:44 +03:00
{
2021-11-30 12:32:58 +02:00
struct prestera_acl_ruleset * ruleset = rule - > ruleset ;
struct prestera_flow_block * block = ruleset - > ht_key . block ;
rhashtable_remove_fast ( & ruleset - > rule_ht , & rule - > ht_node ,
prestera_acl_rule_ht_params ) ;
ruleset - > rule_count - - ;
list_del ( & rule - > list ) ;
prestera_acl_rule_entry_destroy ( sw - > acl , rule - > re ) ;
2022-08-23 14:39:58 +03:00
prestera_acl_ruleset_prio_refresh ( sw - > acl , ruleset ) ;
2021-11-30 12:32:58 +02:00
/* unbind block (all ports) */
2022-02-14 10:20:06 +02:00
if ( ! ruleset - > ht_key . chain_index & & ! ruleset - > rule_count )
2021-11-30 12:32:58 +02:00
prestera_acl_ruleset_block_unbind ( ruleset , block ) ;
2021-06-16 19:01:44 +03:00
}
2021-11-30 12:32:58 +02:00
int prestera_acl_rule_get_stats ( struct prestera_acl * acl ,
struct prestera_acl_rule * rule ,
u64 * packets , u64 * bytes , u64 * last_use )
2021-06-16 19:01:44 +03:00
{
2021-11-30 12:33:00 +02:00
u64 current_packets ;
u64 current_bytes ;
int err ;
err = prestera_counter_stats_get ( acl - > sw - > counter ,
rule - > re - > counter . block ,
rule - > re - > counter . id ,
& current_packets , & current_bytes ) ;
if ( err )
return err ;
* packets = current_packets ;
* bytes = current_bytes ;
2021-11-30 12:32:58 +02:00
* last_use = jiffies ;
return 0 ;
2021-06-16 19:01:44 +03:00
}
2021-11-30 12:32:58 +02:00
struct prestera_acl_rule_entry *
prestera_acl_rule_entry_find ( struct prestera_acl * acl ,
struct prestera_acl_rule_entry_key * key )
2021-06-16 19:01:44 +03:00
{
2021-12-03 15:04:18 +08:00
return rhashtable_lookup_fast ( & acl - > acl_rule_entry_ht , key ,
__prestera_acl_rule_entry_ht_params ) ;
2021-06-16 19:01:44 +03:00
}
2021-11-30 12:32:58 +02:00
static int __prestera_acl_rule_entry2hw_del ( struct prestera_switch * sw ,
struct prestera_acl_rule_entry * e )
2021-06-16 19:01:44 +03:00
{
2021-11-30 12:32:58 +02:00
return prestera_hw_vtcam_rule_del ( sw , e - > vtcam_id , e - > hw_id ) ;
}
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
static int __prestera_acl_rule_entry2hw_add ( struct prestera_switch * sw ,
struct prestera_acl_rule_entry * e )
{
struct prestera_acl_hw_action_info act_hw [ PRESTERA_ACL_RULE_ACTION_MAX ] ;
int act_num ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
memset ( & act_hw , 0 , sizeof ( act_hw ) ) ;
act_num = 0 ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
/* accept */
if ( e - > accept . valid ) {
act_hw [ act_num ] . id = PRESTERA_ACL_RULE_ACTION_ACCEPT ;
act_num + + ;
}
/* drop */
if ( e - > drop . valid ) {
act_hw [ act_num ] . id = PRESTERA_ACL_RULE_ACTION_DROP ;
act_num + + ;
}
/* trap */
if ( e - > trap . valid ) {
act_hw [ act_num ] . id = PRESTERA_ACL_RULE_ACTION_TRAP ;
act_num + + ;
}
2022-04-27 15:05:48 +03:00
/* police */
if ( e - > police . valid ) {
act_hw [ act_num ] . id = PRESTERA_ACL_RULE_ACTION_POLICE ;
act_hw [ act_num ] . police = e - > police . i ;
act_num + + ;
}
2022-02-14 10:20:06 +02:00
/* jump */
if ( e - > jump . valid ) {
act_hw [ act_num ] . id = PRESTERA_ACL_RULE_ACTION_JUMP ;
act_hw [ act_num ] . jump = e - > jump . i ;
act_num + + ;
}
2021-11-30 12:33:00 +02:00
/* counter */
if ( e - > counter . block ) {
act_hw [ act_num ] . id = PRESTERA_ACL_RULE_ACTION_COUNT ;
act_hw [ act_num ] . count . id = e - > counter . id ;
act_num + + ;
}
2021-11-30 12:32:58 +02:00
return prestera_hw_vtcam_rule_add ( sw , e - > vtcam_id , e - > key . prio ,
e - > key . match . key , e - > key . match . mask ,
act_hw , act_num , & e - > hw_id ) ;
2021-06-16 19:01:44 +03:00
}
2021-11-30 12:32:58 +02:00
static void
__prestera_acl_rule_entry_act_destruct ( struct prestera_switch * sw ,
struct prestera_acl_rule_entry * e )
2021-06-16 19:01:44 +03:00
{
2021-11-30 12:33:00 +02:00
/* counter */
prestera_counter_put ( sw - > counter , e - > counter . block , e - > counter . id ) ;
2022-04-27 15:05:48 +03:00
/* police */
if ( e - > police . valid )
prestera_hw_policer_release ( sw , e - > police . i . id ) ;
2021-06-16 19:01:44 +03:00
}
2021-11-30 12:32:58 +02:00
void prestera_acl_rule_entry_destroy ( struct prestera_acl * acl ,
struct prestera_acl_rule_entry * e )
2021-06-16 19:01:44 +03:00
{
2021-11-30 12:32:58 +02:00
int ret ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
rhashtable_remove_fast ( & acl - > acl_rule_entry_ht , & e - > ht_node ,
__prestera_acl_rule_entry_ht_params ) ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
ret = __prestera_acl_rule_entry2hw_del ( acl - > sw , e ) ;
WARN_ON ( ret & & ret ! = - ENODEV ) ;
__prestera_acl_rule_entry_act_destruct ( acl - > sw , e ) ;
kfree ( e ) ;
2021-06-16 19:01:44 +03:00
}
2021-11-30 12:32:58 +02:00
static int
__prestera_acl_rule_entry_act_construct ( struct prestera_switch * sw ,
struct prestera_acl_rule_entry * e ,
struct prestera_acl_rule_entry_arg * arg )
{
2022-04-27 15:05:48 +03:00
int err ;
2021-11-30 12:32:58 +02:00
/* accept */
e - > accept . valid = arg - > accept . valid ;
/* drop */
e - > drop . valid = arg - > drop . valid ;
/* trap */
e - > trap . valid = arg - > trap . valid ;
2022-02-14 10:20:06 +02:00
/* jump */
e - > jump . valid = arg - > jump . valid ;
e - > jump . i = arg - > jump . i ;
2022-04-27 15:05:48 +03:00
/* police */
if ( arg - > police . valid ) {
u8 type = arg - > police . ingress ? PRESTERA_POLICER_TYPE_INGRESS :
PRESTERA_POLICER_TYPE_EGRESS ;
err = prestera_hw_policer_create ( sw , type , & e - > police . i . id ) ;
if ( err )
goto err_out ;
err = prestera_hw_policer_sr_tcm_set ( sw , e - > police . i . id ,
arg - > police . rate ,
arg - > police . burst ) ;
if ( err ) {
prestera_hw_policer_release ( sw , e - > police . i . id ) ;
goto err_out ;
}
e - > police . valid = arg - > police . valid ;
}
2021-11-30 12:33:00 +02:00
/* counter */
if ( arg - > count . valid ) {
err = prestera_counter_get ( sw - > counter , arg - > count . client ,
& e - > counter . block ,
& e - > counter . id ) ;
if ( err )
goto err_out ;
}
2021-11-30 12:32:58 +02:00
return 0 ;
2021-11-30 12:33:00 +02:00
err_out :
__prestera_acl_rule_entry_act_destruct ( sw , e ) ;
return - EINVAL ;
2021-11-30 12:32:58 +02:00
}
struct prestera_acl_rule_entry *
prestera_acl_rule_entry_create ( struct prestera_acl * acl ,
struct prestera_acl_rule_entry_key * key ,
struct prestera_acl_rule_entry_arg * arg )
2021-06-16 19:01:44 +03:00
{
2021-11-30 12:32:58 +02:00
struct prestera_acl_rule_entry * e ;
2021-06-16 19:01:44 +03:00
int err ;
2021-11-30 12:32:58 +02:00
e = kzalloc ( sizeof ( * e ) , GFP_KERNEL ) ;
if ( ! e )
goto err_kzalloc ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
memcpy ( & e - > key , key , sizeof ( * key ) ) ;
e - > vtcam_id = arg - > vtcam_id ;
err = __prestera_acl_rule_entry_act_construct ( acl - > sw , e , arg ) ;
2021-06-16 19:01:44 +03:00
if ( err )
2021-11-30 12:32:58 +02:00
goto err_act_construct ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
err = __prestera_acl_rule_entry2hw_add ( acl - > sw , e ) ;
if ( err )
goto err_hw_add ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
err = rhashtable_insert_fast ( & acl - > acl_rule_entry_ht , & e - > ht_node ,
__prestera_acl_rule_entry_ht_params ) ;
if ( err )
goto err_ht_insert ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
return e ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
err_ht_insert :
WARN_ON ( __prestera_acl_rule_entry2hw_del ( acl - > sw , e ) ) ;
err_hw_add :
__prestera_acl_rule_entry_act_destruct ( acl - > sw , e ) ;
err_act_construct :
kfree ( e ) ;
err_kzalloc :
return NULL ;
2021-06-16 19:01:44 +03:00
}
2021-12-15 12:07:30 +02:00
static int __prestera_acl_vtcam_id_try_fit ( struct prestera_acl * acl , u8 lookup ,
void * keymask , u32 * vtcam_id )
{
struct prestera_acl_vtcam * vtcam ;
int i ;
list_for_each_entry ( vtcam , & acl - > vtcam_list , list ) {
if ( lookup ! = vtcam - > lookup )
continue ;
if ( ! keymask & & ! vtcam - > is_keymask_set )
goto vtcam_found ;
if ( ! ( keymask & & vtcam - > is_keymask_set ) )
continue ;
/* try to fit with vtcam keymask */
for ( i = 0 ; i < __PRESTERA_ACL_RULE_MATCH_TYPE_MAX ; i + + ) {
__be32 __keymask = ( ( __be32 * ) keymask ) [ i ] ;
if ( ! __keymask )
/* vtcam keymask in not interested */
continue ;
if ( __keymask & ~ vtcam - > keymask [ i ] )
/* keymask does not fit the vtcam keymask */
break ;
}
if ( i = = __PRESTERA_ACL_RULE_MATCH_TYPE_MAX )
/* keymask fits vtcam keymask, return it */
goto vtcam_found ;
}
/* nothing is found */
return - ENOENT ;
vtcam_found :
refcount_inc ( & vtcam - > refcount ) ;
* vtcam_id = vtcam - > id ;
return 0 ;
}
2022-06-27 12:50:18 +03:00
int prestera_acl_vtcam_id_get ( struct prestera_acl * acl , u8 lookup , u8 dir ,
2021-11-30 12:32:58 +02:00
void * keymask , u32 * vtcam_id )
2021-06-16 19:01:44 +03:00
{
2021-11-30 12:32:58 +02:00
struct prestera_acl_vtcam * vtcam ;
u32 new_vtcam_id ;
int err ;
/* find the vtcam that suits keymask. We do not expect to have
* a big number of vtcams , so , the list type for vtcam list is
* fine for now
*/
list_for_each_entry ( vtcam , & acl - > vtcam_list , list ) {
2022-06-27 12:50:18 +03:00
if ( lookup ! = vtcam - > lookup | |
dir ! = vtcam - > direction )
2021-11-30 12:32:58 +02:00
continue ;
if ( ! keymask & & ! vtcam - > is_keymask_set ) {
refcount_inc ( & vtcam - > refcount ) ;
goto vtcam_found ;
}
if ( keymask & & vtcam - > is_keymask_set & &
! memcmp ( keymask , vtcam - > keymask , sizeof ( vtcam - > keymask ) ) ) {
refcount_inc ( & vtcam - > refcount ) ;
goto vtcam_found ;
}
}
/* vtcam not found, try to create new one */
vtcam = kzalloc ( sizeof ( * vtcam ) , GFP_KERNEL ) ;
if ( ! vtcam )
return - ENOMEM ;
err = prestera_hw_vtcam_create ( acl - > sw , lookup , keymask , & new_vtcam_id ,
2022-06-27 12:50:18 +03:00
dir ) ;
2021-11-30 12:32:58 +02:00
if ( err ) {
kfree ( vtcam ) ;
2021-12-15 12:07:30 +02:00
/* cannot create new, try to fit into existing vtcam */
if ( __prestera_acl_vtcam_id_try_fit ( acl , lookup ,
keymask , & new_vtcam_id ) )
return err ;
* vtcam_id = new_vtcam_id ;
return 0 ;
2021-11-30 12:32:58 +02:00
}
2022-06-27 12:50:18 +03:00
vtcam - > direction = dir ;
2021-11-30 12:32:58 +02:00
vtcam - > id = new_vtcam_id ;
vtcam - > lookup = lookup ;
if ( keymask ) {
memcpy ( vtcam - > keymask , keymask , sizeof ( vtcam - > keymask ) ) ;
vtcam - > is_keymask_set = true ;
}
refcount_set ( & vtcam - > refcount , 1 ) ;
list_add_rcu ( & vtcam - > list , & acl - > vtcam_list ) ;
vtcam_found :
* vtcam_id = vtcam - > id ;
return 0 ;
2021-06-16 19:01:44 +03:00
}
2021-11-30 12:32:58 +02:00
int prestera_acl_vtcam_id_put ( struct prestera_acl * acl , u32 vtcam_id )
2021-06-16 19:01:44 +03:00
{
2021-11-30 12:32:58 +02:00
struct prestera_acl_vtcam * vtcam ;
2021-06-16 19:01:44 +03:00
int err ;
2021-11-30 12:32:58 +02:00
list_for_each_entry ( vtcam , & acl - > vtcam_list , list ) {
if ( vtcam_id ! = vtcam - > id )
continue ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
if ( ! refcount_dec_and_test ( & vtcam - > refcount ) )
return 0 ;
2021-06-16 19:01:44 +03:00
2021-11-30 12:32:58 +02:00
err = prestera_hw_vtcam_destroy ( acl - > sw , vtcam - > id ) ;
if ( err & & err ! = - ENODEV ) {
refcount_set ( & vtcam - > refcount , 1 ) ;
return err ;
}
list_del ( & vtcam - > list ) ;
kfree ( vtcam ) ;
return 0 ;
}
return - ENOENT ;
2021-06-16 19:01:44 +03:00
}
int prestera_acl_init ( struct prestera_switch * sw )
{
struct prestera_acl * acl ;
2021-11-30 12:32:58 +02:00
int err ;
2021-06-16 19:01:44 +03:00
acl = kzalloc ( sizeof ( * acl ) , GFP_KERNEL ) ;
if ( ! acl )
return - ENOMEM ;
2021-11-30 12:32:58 +02:00
acl - > sw = sw ;
2021-06-16 19:01:44 +03:00
INIT_LIST_HEAD ( & acl - > rules ) ;
2021-11-30 12:32:58 +02:00
INIT_LIST_HEAD ( & acl - > vtcam_list ) ;
idr_init ( & acl - > uid ) ;
err = rhashtable_init ( & acl - > acl_rule_entry_ht ,
& __prestera_acl_rule_entry_ht_params ) ;
if ( err )
goto err_acl_rule_entry_ht_init ;
err = rhashtable_init ( & acl - > ruleset_ht ,
& prestera_acl_ruleset_ht_params ) ;
if ( err )
goto err_ruleset_ht_init ;
2021-06-16 19:01:44 +03:00
sw - > acl = acl ;
return 0 ;
2021-11-30 12:32:58 +02:00
err_ruleset_ht_init :
rhashtable_destroy ( & acl - > acl_rule_entry_ht ) ;
err_acl_rule_entry_ht_init :
kfree ( acl ) ;
return err ;
2021-06-16 19:01:44 +03:00
}
void prestera_acl_fini ( struct prestera_switch * sw )
{
struct prestera_acl * acl = sw - > acl ;
2021-11-30 12:32:58 +02:00
WARN_ON ( ! idr_is_empty ( & acl - > uid ) ) ;
idr_destroy ( & acl - > uid ) ;
WARN_ON ( ! list_empty ( & acl - > vtcam_list ) ) ;
2021-06-16 19:01:44 +03:00
WARN_ON ( ! list_empty ( & acl - > rules ) ) ;
2021-11-30 12:32:58 +02:00
rhashtable_destroy ( & acl - > ruleset_ht ) ;
rhashtable_destroy ( & acl - > acl_rule_entry_ht ) ;
2021-06-16 19:01:44 +03:00
kfree ( acl ) ;
}