2005-04-17 02:20:36 +04:00
/*
* net / sched / police . c Input police filter .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
* Authors : Alexey Kuznetsov , < kuznet @ ms2 . inr . ac . ru >
* J Hadi Salim ( action changes )
*/
# include <asm/uaccess.h>
# include <asm/system.h>
# include <linux/bitops.h>
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/socket.h>
# include <linux/sockios.h>
# include <linux/in.h>
# include <linux/errno.h>
# include <linux/interrupt.h>
# include <linux/netdevice.h>
# include <linux/skbuff.h>
# include <linux/module.h>
# include <linux/rtnetlink.h>
# include <linux/init.h>
# include <net/sock.h>
# include <net/act_api.h>
2006-08-22 10:54:55 +04:00
# define L2T(p,L) ((p)->tcfp_R_tab->data[(L)>>(p)->tcfp_R_tab->rate.cell_log])
# define L2T_P(p,L) ((p)->tcfp_P_tab->data[(L)>>(p)->tcfp_P_tab->rate.cell_log])
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
# define POL_TAB_MASK 15
static struct tcf_common * tcf_police_ht [ POL_TAB_MASK + 1 ] ;
static u32 police_idx_gen ;
static DEFINE_RWLOCK ( police_lock ) ;
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
static struct tcf_hashinfo police_hash_info = {
. htab = tcf_police_ht ,
. hmask = POL_TAB_MASK ,
. lock = & police_lock ,
} ;
2005-04-17 02:20:36 +04:00
2006-12-01 06:54:05 +03:00
/* old policer structure from before tc actions */
struct tc_police_compat
{
u32 index ;
int action ;
u32 limit ;
u32 burst ;
u32 mtu ;
struct tc_ratespec rate ;
struct tc_ratespec peakrate ;
} ;
2006-08-22 10:54:55 +04:00
/* Each policer is serialized by its individual spinlock */
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_NET_CLS_ACT
2006-04-07 09:24:22 +04:00
static int tcf_act_police_walker ( struct sk_buff * skb , struct netlink_callback * cb ,
2007-02-09 17:25:16 +03:00
int type , struct tc_action * a )
2005-04-17 02:20:36 +04:00
{
2006-08-22 10:54:55 +04:00
struct tcf_common * p ;
2005-04-17 02:20:36 +04:00
int err = 0 , index = - 1 , i = 0 , s_i = 0 , n_i = 0 ;
struct rtattr * r ;
read_lock ( & police_lock ) ;
s_i = cb - > args [ 0 ] ;
2006-08-22 10:54:55 +04:00
for ( i = 0 ; i < ( POL_TAB_MASK + 1 ) ; i + + ) {
p = tcf_police_ht [ tcf_hash ( i , POL_TAB_MASK ) ] ;
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
for ( ; p ; p = p - > tcfc_next ) {
2005-04-17 02:20:36 +04:00
index + + ;
if ( index < s_i )
continue ;
a - > priv = p ;
a - > order = index ;
r = ( struct rtattr * ) skb - > tail ;
RTA_PUT ( skb , a - > order , 0 , NULL ) ;
if ( type = = RTM_DELACTION )
err = tcf_action_dump_1 ( skb , a , 0 , 1 ) ;
else
err = tcf_action_dump_1 ( skb , a , 0 , 0 ) ;
if ( err < 0 ) {
index - - ;
skb_trim ( skb , ( u8 * ) r - skb - > data ) ;
goto done ;
}
r - > rta_len = skb - > tail - ( u8 * ) r ;
n_i + + ;
}
}
done :
read_unlock ( & police_lock ) ;
if ( n_i )
cb - > args [ 0 ] + = n_i ;
return n_i ;
rtattr_failure :
skb_trim ( skb , ( u8 * ) r - skb - > data ) ;
goto done ;
}
# endif
void tcf_police_destroy ( struct tcf_police * p )
{
2006-08-22 10:54:55 +04:00
unsigned int h = tcf_hash ( p - > tcf_index , POL_TAB_MASK ) ;
struct tcf_common * * p1p ;
2007-02-09 17:25:16 +03:00
2006-08-22 10:54:55 +04:00
for ( p1p = & tcf_police_ht [ h ] ; * p1p ; p1p = & ( * p1p ) - > tcfc_next ) {
if ( * p1p = = & p - > common ) {
2005-04-17 02:20:36 +04:00
write_lock_bh ( & police_lock ) ;
2006-08-22 10:54:55 +04:00
* p1p = p - > tcf_next ;
2005-04-17 02:20:36 +04:00
write_unlock_bh ( & police_lock ) ;
# ifdef CONFIG_NET_ESTIMATOR
2006-08-22 10:54:55 +04:00
gen_kill_estimator ( & p - > tcf_bstats ,
& p - > tcf_rate_est ) ;
2005-04-17 02:20:36 +04:00
# endif
2006-08-22 10:54:55 +04:00
if ( p - > tcfp_R_tab )
qdisc_put_rtab ( p - > tcfp_R_tab ) ;
if ( p - > tcfp_P_tab )
qdisc_put_rtab ( p - > tcfp_P_tab ) ;
2005-04-17 02:20:36 +04:00
kfree ( p ) ;
return ;
}
}
BUG_TRAP ( 0 ) ;
}
# ifdef CONFIG_NET_CLS_ACT
static int tcf_act_police_locate ( struct rtattr * rta , struct rtattr * est ,
2007-02-09 17:25:16 +03:00
struct tc_action * a , int ovr , int bind )
2005-04-17 02:20:36 +04:00
{
unsigned h ;
int ret = 0 , err ;
struct rtattr * tb [ TCA_POLICE_MAX ] ;
struct tc_police * parm ;
2006-08-22 10:54:55 +04:00
struct tcf_police * police ;
2005-04-17 02:20:36 +04:00
struct qdisc_rate_table * R_tab = NULL , * P_tab = NULL ;
2006-12-01 06:54:05 +03:00
int size ;
2005-04-17 02:20:36 +04:00
if ( rta = = NULL | | rtattr_parse_nested ( tb , TCA_POLICE_MAX , rta ) < 0 )
return - EINVAL ;
2006-12-01 06:54:05 +03:00
if ( tb [ TCA_POLICE_TBF - 1 ] = = NULL )
return - EINVAL ;
size = RTA_PAYLOAD ( tb [ TCA_POLICE_TBF - 1 ] ) ;
if ( size ! = sizeof ( * parm ) & & size ! = sizeof ( struct tc_police_compat ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
parm = RTA_DATA ( tb [ TCA_POLICE_TBF - 1 ] ) ;
if ( tb [ TCA_POLICE_RESULT - 1 ] ! = NULL & &
RTA_PAYLOAD ( tb [ TCA_POLICE_RESULT - 1 ] ) ! = sizeof ( u32 ) )
return - EINVAL ;
if ( tb [ TCA_POLICE_RESULT - 1 ] ! = NULL & &
RTA_PAYLOAD ( tb [ TCA_POLICE_RESULT - 1 ] ) ! = sizeof ( u32 ) )
return - EINVAL ;
2006-08-22 10:54:55 +04:00
if ( parm - > index ) {
struct tcf_common * pc ;
pc = tcf_hash_lookup ( parm - > index , & police_hash_info ) ;
if ( pc ! = NULL ) {
a - > priv = pc ;
police = to_police ( pc ) ;
if ( bind ) {
police - > tcf_bindcnt + = 1 ;
police - > tcf_refcnt + = 1 ;
}
if ( ovr )
goto override ;
return ret ;
2005-04-17 02:20:36 +04:00
}
}
2006-08-22 10:54:55 +04:00
police = kzalloc ( sizeof ( * police ) , GFP_KERNEL ) ;
if ( police = = NULL )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
ret = ACT_P_CREATED ;
2006-08-22 10:54:55 +04:00
police - > tcf_refcnt = 1 ;
spin_lock_init ( & police - > tcf_lock ) ;
police - > tcf_stats_lock = & police - > tcf_lock ;
2005-04-17 02:20:36 +04:00
if ( bind )
2006-08-22 10:54:55 +04:00
police - > tcf_bindcnt = 1 ;
2005-04-17 02:20:36 +04:00
override :
if ( parm - > rate . rate ) {
err = - ENOMEM ;
R_tab = qdisc_get_rtab ( & parm - > rate , tb [ TCA_POLICE_RATE - 1 ] ) ;
if ( R_tab = = NULL )
goto failure ;
if ( parm - > peakrate . rate ) {
P_tab = qdisc_get_rtab ( & parm - > peakrate ,
tb [ TCA_POLICE_PEAKRATE - 1 ] ) ;
2006-08-22 10:54:55 +04:00
if ( P_tab = = NULL ) {
2005-04-17 02:20:36 +04:00
qdisc_put_rtab ( R_tab ) ;
goto failure ;
}
}
}
/* No failure allowed after this point */
2006-08-22 10:54:55 +04:00
spin_lock_bh ( & police - > tcf_lock ) ;
2005-04-17 02:20:36 +04:00
if ( R_tab ! = NULL ) {
2006-08-22 10:54:55 +04:00
qdisc_put_rtab ( police - > tcfp_R_tab ) ;
police - > tcfp_R_tab = R_tab ;
2005-04-17 02:20:36 +04:00
}
if ( P_tab ! = NULL ) {
2006-08-22 10:54:55 +04:00
qdisc_put_rtab ( police - > tcfp_P_tab ) ;
police - > tcfp_P_tab = P_tab ;
2005-04-17 02:20:36 +04:00
}
if ( tb [ TCA_POLICE_RESULT - 1 ] )
2006-08-22 10:54:55 +04:00
police - > tcfp_result = * ( u32 * ) RTA_DATA ( tb [ TCA_POLICE_RESULT - 1 ] ) ;
police - > tcfp_toks = police - > tcfp_burst = parm - > burst ;
police - > tcfp_mtu = parm - > mtu ;
if ( police - > tcfp_mtu = = 0 ) {
police - > tcfp_mtu = ~ 0 ;
if ( police - > tcfp_R_tab )
police - > tcfp_mtu = 255 < < police - > tcfp_R_tab - > rate . cell_log ;
2005-04-17 02:20:36 +04:00
}
2006-08-22 10:54:55 +04:00
if ( police - > tcfp_P_tab )
police - > tcfp_ptoks = L2T_P ( police , police - > tcfp_mtu ) ;
police - > tcf_action = parm - > action ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_NET_ESTIMATOR
if ( tb [ TCA_POLICE_AVRATE - 1 ] )
2006-08-22 10:54:55 +04:00
police - > tcfp_ewma_rate =
* ( u32 * ) RTA_DATA ( tb [ TCA_POLICE_AVRATE - 1 ] ) ;
2005-04-17 02:20:36 +04:00
if ( est )
2006-08-22 10:54:55 +04:00
gen_replace_estimator ( & police - > tcf_bstats ,
& police - > tcf_rate_est ,
police - > tcf_stats_lock , est ) ;
2005-04-17 02:20:36 +04:00
# endif
2006-08-22 10:54:55 +04:00
spin_unlock_bh ( & police - > tcf_lock ) ;
2005-04-17 02:20:36 +04:00
if ( ret ! = ACT_P_CREATED )
return ret ;
2006-08-22 10:54:55 +04:00
PSCHED_GET_TIME ( police - > tcfp_t_c ) ;
police - > tcf_index = parm - > index ? parm - > index :
tcf_hash_new_index ( & police_idx_gen , & police_hash_info ) ;
h = tcf_hash ( police - > tcf_index , POL_TAB_MASK ) ;
2005-04-17 02:20:36 +04:00
write_lock_bh ( & police_lock ) ;
2006-08-22 10:54:55 +04:00
police - > tcf_next = tcf_police_ht [ h ] ;
tcf_police_ht [ h ] = & police - > common ;
2005-04-17 02:20:36 +04:00
write_unlock_bh ( & police_lock ) ;
2006-08-22 10:54:55 +04:00
a - > priv = police ;
2005-04-17 02:20:36 +04:00
return ret ;
failure :
if ( ret = = ACT_P_CREATED )
2006-08-22 10:54:55 +04:00
kfree ( police ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
static int tcf_act_police_cleanup ( struct tc_action * a , int bind )
{
2006-08-22 10:54:55 +04:00
struct tcf_police * p = a - > priv ;
2005-04-17 02:20:36 +04:00
if ( p ! = NULL )
return tcf_police_release ( p , bind ) ;
return 0 ;
}
2006-01-09 09:15:34 +03:00
static int tcf_act_police ( struct sk_buff * skb , struct tc_action * a ,
2007-02-09 17:25:16 +03:00
struct tcf_result * res )
2005-04-17 02:20:36 +04:00
{
2006-08-22 10:54:55 +04:00
struct tcf_police * police = a - > priv ;
2005-04-17 02:20:36 +04:00
psched_time_t now ;
long toks ;
long ptoks = 0 ;
2006-08-22 10:54:55 +04:00
spin_lock ( & police - > tcf_lock ) ;
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
police - > tcf_bstats . bytes + = skb - > len ;
police - > tcf_bstats . packets + + ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_NET_ESTIMATOR
2006-08-22 10:54:55 +04:00
if ( police - > tcfp_ewma_rate & &
police - > tcf_rate_est . bps > = police - > tcfp_ewma_rate ) {
police - > tcf_qstats . overlimits + + ;
spin_unlock ( & police - > tcf_lock ) ;
return police - > tcf_action ;
2005-04-17 02:20:36 +04:00
}
# endif
2006-08-22 10:54:55 +04:00
if ( skb - > len < = police - > tcfp_mtu ) {
if ( police - > tcfp_R_tab = = NULL ) {
spin_unlock ( & police - > tcf_lock ) ;
return police - > tcfp_result ;
2005-04-17 02:20:36 +04:00
}
PSCHED_GET_TIME ( now ) ;
2006-08-22 10:54:55 +04:00
toks = PSCHED_TDIFF_SAFE ( now , police - > tcfp_t_c ,
police - > tcfp_burst ) ;
if ( police - > tcfp_P_tab ) {
ptoks = toks + police - > tcfp_ptoks ;
if ( ptoks > ( long ) L2T_P ( police , police - > tcfp_mtu ) )
ptoks = ( long ) L2T_P ( police , police - > tcfp_mtu ) ;
ptoks - = L2T_P ( police , skb - > len ) ;
2005-04-17 02:20:36 +04:00
}
2006-08-22 10:54:55 +04:00
toks + = police - > tcfp_toks ;
if ( toks > ( long ) police - > tcfp_burst )
toks = police - > tcfp_burst ;
toks - = L2T ( police , skb - > len ) ;
2005-04-17 02:20:36 +04:00
if ( ( toks | ptoks ) > = 0 ) {
2006-08-22 10:54:55 +04:00
police - > tcfp_t_c = now ;
police - > tcfp_toks = toks ;
police - > tcfp_ptoks = ptoks ;
spin_unlock ( & police - > tcf_lock ) ;
return police - > tcfp_result ;
2005-04-17 02:20:36 +04:00
}
}
2006-08-22 10:54:55 +04:00
police - > tcf_qstats . overlimits + + ;
spin_unlock ( & police - > tcf_lock ) ;
return police - > tcf_action ;
2005-04-17 02:20:36 +04:00
}
static int
tcf_act_police_dump ( struct sk_buff * skb , struct tc_action * a , int bind , int ref )
{
unsigned char * b = skb - > tail ;
2006-08-22 10:54:55 +04:00
struct tcf_police * police = a - > priv ;
2005-04-17 02:20:36 +04:00
struct tc_police opt ;
2006-08-22 10:54:55 +04:00
opt . index = police - > tcf_index ;
opt . action = police - > tcf_action ;
opt . mtu = police - > tcfp_mtu ;
opt . burst = police - > tcfp_burst ;
opt . refcnt = police - > tcf_refcnt - ref ;
opt . bindcnt = police - > tcf_bindcnt - bind ;
if ( police - > tcfp_R_tab )
opt . rate = police - > tcfp_R_tab - > rate ;
2005-04-17 02:20:36 +04:00
else
memset ( & opt . rate , 0 , sizeof ( opt . rate ) ) ;
2006-08-22 10:54:55 +04:00
if ( police - > tcfp_P_tab )
opt . peakrate = police - > tcfp_P_tab - > rate ;
2005-04-17 02:20:36 +04:00
else
memset ( & opt . peakrate , 0 , sizeof ( opt . peakrate ) ) ;
RTA_PUT ( skb , TCA_POLICE_TBF , sizeof ( opt ) , & opt ) ;
2006-08-22 10:54:55 +04:00
if ( police - > tcfp_result )
RTA_PUT ( skb , TCA_POLICE_RESULT , sizeof ( int ) ,
& police - > tcfp_result ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_NET_ESTIMATOR
2006-08-22 10:54:55 +04:00
if ( police - > tcfp_ewma_rate )
RTA_PUT ( skb , TCA_POLICE_AVRATE , 4 , & police - > tcfp_ewma_rate ) ;
2005-04-17 02:20:36 +04:00
# endif
return skb - > len ;
rtattr_failure :
skb_trim ( skb , b - skb - > data ) ;
return - 1 ;
}
MODULE_AUTHOR ( " Alexey Kuznetsov " ) ;
MODULE_DESCRIPTION ( " Policing actions " ) ;
MODULE_LICENSE ( " GPL " ) ;
static struct tc_action_ops act_police_ops = {
. kind = " police " ,
2006-08-22 10:54:55 +04:00
. hinfo = & police_hash_info ,
2005-04-17 02:20:36 +04:00
. type = TCA_ID_POLICE ,
. capab = TCA_CAP_NONE ,
. owner = THIS_MODULE ,
. act = tcf_act_police ,
. dump = tcf_act_police_dump ,
. cleanup = tcf_act_police_cleanup ,
2006-08-22 10:54:55 +04:00
. lookup = tcf_hash_search ,
2005-04-17 02:20:36 +04:00
. init = tcf_act_police_locate ,
2006-04-07 09:24:22 +04:00
. walk = tcf_act_police_walker
2005-04-17 02:20:36 +04:00
} ;
static int __init
police_init_module ( void )
{
return tcf_register_action ( & act_police_ops ) ;
}
static void __exit
police_cleanup_module ( void )
{
tcf_unregister_action ( & act_police_ops ) ;
}
module_init ( police_init_module ) ;
module_exit ( police_cleanup_module ) ;
2006-01-09 09:16:25 +03:00
# else /* CONFIG_NET_CLS_ACT */
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
static struct tcf_common * tcf_police_lookup ( u32 index )
2005-04-17 02:20:36 +04:00
{
2006-08-22 10:54:55 +04:00
struct tcf_hashinfo * hinfo = & police_hash_info ;
struct tcf_common * p ;
read_lock ( hinfo - > lock ) ;
for ( p = hinfo - > htab [ tcf_hash ( index , hinfo - > hmask ) ] ; p ;
p = p - > tcfc_next ) {
if ( p - > tcfc_index = = index )
break ;
}
read_unlock ( hinfo - > lock ) ;
return p ;
}
static u32 tcf_police_new_index ( void )
{
u32 * idx_gen = & police_idx_gen ;
u32 val = * idx_gen ;
do {
if ( + + val = = 0 )
val = 1 ;
} while ( tcf_police_lookup ( val ) ) ;
return ( * idx_gen = val ) ;
}
struct tcf_police * tcf_police_locate ( struct rtattr * rta , struct rtattr * est )
{
unsigned int h ;
struct tcf_police * police ;
2005-04-17 02:20:36 +04:00
struct rtattr * tb [ TCA_POLICE_MAX ] ;
struct tc_police * parm ;
2006-12-01 06:54:05 +03:00
int size ;
2005-04-17 02:20:36 +04:00
if ( rtattr_parse_nested ( tb , TCA_POLICE_MAX , rta ) < 0 )
return NULL ;
2006-12-01 06:54:05 +03:00
if ( tb [ TCA_POLICE_TBF - 1 ] = = NULL )
return NULL ;
size = RTA_PAYLOAD ( tb [ TCA_POLICE_TBF - 1 ] ) ;
if ( size ! = sizeof ( * parm ) & & size ! = sizeof ( struct tc_police_compat ) )
2005-04-17 02:20:36 +04:00
return NULL ;
parm = RTA_DATA ( tb [ TCA_POLICE_TBF - 1 ] ) ;
2006-08-22 10:54:55 +04:00
if ( parm - > index ) {
struct tcf_common * pc ;
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
pc = tcf_police_lookup ( parm - > index ) ;
if ( pc ) {
police = to_police ( pc ) ;
police - > tcf_refcnt + + ;
return police ;
}
}
police = kzalloc ( sizeof ( * police ) , GFP_KERNEL ) ;
if ( unlikely ( ! police ) )
2005-04-17 02:20:36 +04:00
return NULL ;
2006-08-22 10:54:55 +04:00
police - > tcf_refcnt = 1 ;
spin_lock_init ( & police - > tcf_lock ) ;
police - > tcf_stats_lock = & police - > tcf_lock ;
2005-04-17 02:20:36 +04:00
if ( parm - > rate . rate ) {
2006-08-22 10:54:55 +04:00
police - > tcfp_R_tab =
qdisc_get_rtab ( & parm - > rate , tb [ TCA_POLICE_RATE - 1 ] ) ;
if ( police - > tcfp_R_tab = = NULL )
2005-04-17 02:20:36 +04:00
goto failure ;
if ( parm - > peakrate . rate ) {
2006-08-22 10:54:55 +04:00
police - > tcfp_P_tab =
qdisc_get_rtab ( & parm - > peakrate ,
tb [ TCA_POLICE_PEAKRATE - 1 ] ) ;
if ( police - > tcfp_P_tab = = NULL )
2005-04-17 02:20:36 +04:00
goto failure ;
}
}
if ( tb [ TCA_POLICE_RESULT - 1 ] ) {
if ( RTA_PAYLOAD ( tb [ TCA_POLICE_RESULT - 1 ] ) ! = sizeof ( u32 ) )
goto failure ;
2006-08-22 10:54:55 +04:00
police - > tcfp_result = * ( u32 * ) RTA_DATA ( tb [ TCA_POLICE_RESULT - 1 ] ) ;
2005-04-17 02:20:36 +04:00
}
# ifdef CONFIG_NET_ESTIMATOR
if ( tb [ TCA_POLICE_AVRATE - 1 ] ) {
if ( RTA_PAYLOAD ( tb [ TCA_POLICE_AVRATE - 1 ] ) ! = sizeof ( u32 ) )
goto failure ;
2006-08-22 10:54:55 +04:00
police - > tcfp_ewma_rate =
* ( u32 * ) RTA_DATA ( tb [ TCA_POLICE_AVRATE - 1 ] ) ;
2005-04-17 02:20:36 +04:00
}
# endif
2006-08-22 10:54:55 +04:00
police - > tcfp_toks = police - > tcfp_burst = parm - > burst ;
police - > tcfp_mtu = parm - > mtu ;
if ( police - > tcfp_mtu = = 0 ) {
police - > tcfp_mtu = ~ 0 ;
if ( police - > tcfp_R_tab )
police - > tcfp_mtu = 255 < < police - > tcfp_R_tab - > rate . cell_log ;
2005-04-17 02:20:36 +04:00
}
2006-08-22 10:54:55 +04:00
if ( police - > tcfp_P_tab )
police - > tcfp_ptoks = L2T_P ( police , police - > tcfp_mtu ) ;
PSCHED_GET_TIME ( police - > tcfp_t_c ) ;
police - > tcf_index = parm - > index ? parm - > index :
tcf_police_new_index ( ) ;
police - > tcf_action = parm - > action ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_NET_ESTIMATOR
if ( est )
2006-08-22 10:54:55 +04:00
gen_new_estimator ( & police - > tcf_bstats , & police - > tcf_rate_est ,
police - > tcf_stats_lock , est ) ;
2005-04-17 02:20:36 +04:00
# endif
2006-08-22 10:54:55 +04:00
h = tcf_hash ( police - > tcf_index , POL_TAB_MASK ) ;
2005-04-17 02:20:36 +04:00
write_lock_bh ( & police_lock ) ;
2006-08-22 10:54:55 +04:00
police - > tcf_next = tcf_police_ht [ h ] ;
tcf_police_ht [ h ] = & police - > common ;
2005-04-17 02:20:36 +04:00
write_unlock_bh ( & police_lock ) ;
2006-08-22 10:54:55 +04:00
return police ;
2005-04-17 02:20:36 +04:00
failure :
2006-08-22 10:54:55 +04:00
if ( police - > tcfp_R_tab )
qdisc_put_rtab ( police - > tcfp_R_tab ) ;
kfree ( police ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
2006-08-22 10:54:55 +04:00
int tcf_police ( struct sk_buff * skb , struct tcf_police * police )
2005-04-17 02:20:36 +04:00
{
psched_time_t now ;
long toks ;
long ptoks = 0 ;
2006-08-22 10:54:55 +04:00
spin_lock ( & police - > tcf_lock ) ;
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
police - > tcf_bstats . bytes + = skb - > len ;
police - > tcf_bstats . packets + + ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_NET_ESTIMATOR
2006-08-22 10:54:55 +04:00
if ( police - > tcfp_ewma_rate & &
police - > tcf_rate_est . bps > = police - > tcfp_ewma_rate ) {
police - > tcf_qstats . overlimits + + ;
spin_unlock ( & police - > tcf_lock ) ;
return police - > tcf_action ;
2005-04-17 02:20:36 +04:00
}
# endif
2006-08-22 10:54:55 +04:00
if ( skb - > len < = police - > tcfp_mtu ) {
if ( police - > tcfp_R_tab = = NULL ) {
spin_unlock ( & police - > tcf_lock ) ;
return police - > tcfp_result ;
2005-04-17 02:20:36 +04:00
}
PSCHED_GET_TIME ( now ) ;
2006-08-22 10:54:55 +04:00
toks = PSCHED_TDIFF_SAFE ( now , police - > tcfp_t_c ,
police - > tcfp_burst ) ;
if ( police - > tcfp_P_tab ) {
ptoks = toks + police - > tcfp_ptoks ;
if ( ptoks > ( long ) L2T_P ( police , police - > tcfp_mtu ) )
ptoks = ( long ) L2T_P ( police , police - > tcfp_mtu ) ;
ptoks - = L2T_P ( police , skb - > len ) ;
2005-04-17 02:20:36 +04:00
}
2006-08-22 10:54:55 +04:00
toks + = police - > tcfp_toks ;
if ( toks > ( long ) police - > tcfp_burst )
toks = police - > tcfp_burst ;
toks - = L2T ( police , skb - > len ) ;
2005-04-17 02:20:36 +04:00
if ( ( toks | ptoks ) > = 0 ) {
2006-08-22 10:54:55 +04:00
police - > tcfp_t_c = now ;
police - > tcfp_toks = toks ;
police - > tcfp_ptoks = ptoks ;
spin_unlock ( & police - > tcf_lock ) ;
return police - > tcfp_result ;
2005-04-17 02:20:36 +04:00
}
}
2006-08-22 10:54:55 +04:00
police - > tcf_qstats . overlimits + + ;
spin_unlock ( & police - > tcf_lock ) ;
return police - > tcf_action ;
2005-04-17 02:20:36 +04:00
}
2006-01-09 09:16:25 +03:00
EXPORT_SYMBOL ( tcf_police ) ;
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
int tcf_police_dump ( struct sk_buff * skb , struct tcf_police * police )
2005-04-17 02:20:36 +04:00
{
2006-08-22 10:54:55 +04:00
unsigned char * b = skb - > tail ;
2005-04-17 02:20:36 +04:00
struct tc_police opt ;
2006-08-22 10:54:55 +04:00
opt . index = police - > tcf_index ;
opt . action = police - > tcf_action ;
opt . mtu = police - > tcfp_mtu ;
opt . burst = police - > tcfp_burst ;
if ( police - > tcfp_R_tab )
opt . rate = police - > tcfp_R_tab - > rate ;
2005-04-17 02:20:36 +04:00
else
memset ( & opt . rate , 0 , sizeof ( opt . rate ) ) ;
2006-08-22 10:54:55 +04:00
if ( police - > tcfp_P_tab )
opt . peakrate = police - > tcfp_P_tab - > rate ;
2005-04-17 02:20:36 +04:00
else
memset ( & opt . peakrate , 0 , sizeof ( opt . peakrate ) ) ;
RTA_PUT ( skb , TCA_POLICE_TBF , sizeof ( opt ) , & opt ) ;
2006-08-22 10:54:55 +04:00
if ( police - > tcfp_result )
RTA_PUT ( skb , TCA_POLICE_RESULT , sizeof ( int ) ,
& police - > tcfp_result ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_NET_ESTIMATOR
2006-08-22 10:54:55 +04:00
if ( police - > tcfp_ewma_rate )
RTA_PUT ( skb , TCA_POLICE_AVRATE , 4 , & police - > tcfp_ewma_rate ) ;
2005-04-17 02:20:36 +04:00
# endif
return skb - > len ;
rtattr_failure :
skb_trim ( skb , b - skb - > data ) ;
return - 1 ;
}
2006-08-22 10:54:55 +04:00
int tcf_police_dump_stats ( struct sk_buff * skb , struct tcf_police * police )
2005-04-17 02:20:36 +04:00
{
struct gnet_dump d ;
2007-02-09 17:25:16 +03:00
2005-04-17 02:20:36 +04:00
if ( gnet_stats_start_copy_compat ( skb , TCA_STATS2 , TCA_STATS ,
2006-08-22 10:54:55 +04:00
TCA_XSTATS , police - > tcf_stats_lock ,
& d ) < 0 )
2005-04-17 02:20:36 +04:00
goto errout ;
2007-02-09 17:25:16 +03:00
2006-08-22 10:54:55 +04:00
if ( gnet_stats_copy_basic ( & d , & police - > tcf_bstats ) < 0 | |
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_NET_ESTIMATOR
2006-08-22 10:54:55 +04:00
gnet_stats_copy_rate_est ( & d , & police - > tcf_rate_est ) < 0 | |
2005-04-17 02:20:36 +04:00
# endif
2006-08-22 10:54:55 +04:00
gnet_stats_copy_queue ( & d , & police - > tcf_qstats ) < 0 )
2005-04-17 02:20:36 +04:00
goto errout ;
if ( gnet_stats_finish_copy ( & d ) < 0 )
goto errout ;
return 0 ;
errout :
return - 1 ;
}
2006-01-09 09:16:25 +03:00
# endif /* CONFIG_NET_CLS_ACT */