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 <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/skbuff.h>
# include <linux/rtnetlink.h>
# include <linux/init.h>
# include <net/act_api.h>
2007-03-26 10:06:12 +04:00
# include <net/netlink.h>
2005-04-17 02:20:36 +04:00
2007-09-12 18:35:24 +04:00
# define L2T(p,L) qdisc_l2t((p)->tcfp_R_tab, L)
# define L2T_P(p,L) qdisc_l2t((p)->tcfp_P_tab, L)
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
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 ;
2008-01-24 07:34:11 +03:00
struct nlattr * nest ;
2005-04-17 02:20:36 +04:00
[NET_SCHED] protect action config/dump from irqs
(with no apologies to C Heston)
On Mon, 2007-10-09 at 21:00 +0800, Herbert Xu wrote:
On Sun, Sep 02, 2007 at 01:11:29PM +0000, Christian Kujau wrote:
> >
> > after upgrading to 2.6.23-rc5 (and applying davem's fix [0]), lockdep
> > was quite noisy when I tried to shape my external (wireless) interface:
> >
> > [ 6400.534545] FahCore_78.exe/3552 just changed the state of lock:
> > [ 6400.534713] (&dev->ingress_lock){-+..}, at: [<c038d595>]
> > netif_receive_skb+0x2d5/0x3c0
> > [ 6400.534941] but this lock took another, soft-read-irq-unsafe lock in the
> > past:
> > [ 6400.535145] (police_lock){-.--}
>
> This is a genuine dead-lock. The police lock can be taken
> for reading with softirqs on. If a second CPU tries to take
> the police lock for writing, while holding the ingress lock,
> then a softirq on the first CPU can dead-lock when it tries
> to get the ingress lock.
Signed-off-by: Jamal Hadi Salim <hadi@cyberus.ca>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-09-12 18:32:59 +04:00
read_lock_bh ( & police_lock ) ;
2005-04-17 02:20:36 +04:00
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 ;
2008-01-24 07:34:11 +03:00
nest = nla_nest_start ( skb , a - > order ) ;
if ( nest = = NULL )
goto nla_put_failure ;
2005-04-17 02:20:36 +04:00
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 - - ;
2008-01-24 07:34:11 +03:00
nla_nest_cancel ( skb , nest ) ;
2005-04-17 02:20:36 +04:00
goto done ;
}
2008-01-24 07:34:11 +03:00
nla_nest_end ( skb , nest ) ;
2005-04-17 02:20:36 +04:00
n_i + + ;
}
}
done :
[NET_SCHED] protect action config/dump from irqs
(with no apologies to C Heston)
On Mon, 2007-10-09 at 21:00 +0800, Herbert Xu wrote:
On Sun, Sep 02, 2007 at 01:11:29PM +0000, Christian Kujau wrote:
> >
> > after upgrading to 2.6.23-rc5 (and applying davem's fix [0]), lockdep
> > was quite noisy when I tried to shape my external (wireless) interface:
> >
> > [ 6400.534545] FahCore_78.exe/3552 just changed the state of lock:
> > [ 6400.534713] (&dev->ingress_lock){-+..}, at: [<c038d595>]
> > netif_receive_skb+0x2d5/0x3c0
> > [ 6400.534941] but this lock took another, soft-read-irq-unsafe lock in the
> > past:
> > [ 6400.535145] (police_lock){-.--}
>
> This is a genuine dead-lock. The police lock can be taken
> for reading with softirqs on. If a second CPU tries to take
> the police lock for writing, while holding the ingress lock,
> then a softirq on the first CPU can dead-lock when it tries
> to get the ingress lock.
Signed-off-by: Jamal Hadi Salim <hadi@cyberus.ca>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-09-12 18:32:59 +04:00
read_unlock_bh ( & police_lock ) ;
2005-04-17 02:20:36 +04:00
if ( n_i )
cb - > args [ 0 ] + = n_i ;
return n_i ;
2008-01-23 09:11:50 +03:00
nla_put_failure :
2008-01-24 07:34:11 +03:00
nla_nest_cancel ( skb , nest ) ;
2005-04-17 02:20:36 +04:00
goto done ;
}
2007-07-15 11:03:05 +04:00
static void tcf_police_destroy ( struct tcf_police * p )
2005-04-17 02:20:36 +04:00
{
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 ) ;
2006-08-22 10:54:55 +04:00
gen_kill_estimator ( & p - > tcf_bstats ,
& p - > tcf_rate_est ) ;
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 ;
}
}
2008-07-26 08:43:18 +04:00
WARN_ON ( 1 ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-24 07:36:30 +03:00
static const struct nla_policy police_policy [ TCA_POLICE_MAX + 1 ] = {
[ TCA_POLICE_RATE ] = { . len = TC_RTAB_SIZE } ,
[ TCA_POLICE_PEAKRATE ] = { . len = TC_RTAB_SIZE } ,
[ TCA_POLICE_AVRATE ] = { . type = NLA_U32 } ,
[ TCA_POLICE_RESULT ] = { . type = NLA_U32 } ,
} ;
2008-01-23 09:11:50 +03:00
static int tcf_act_police_locate ( struct nlattr * nla , struct nlattr * 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 ;
2008-01-23 09:11:50 +03:00
struct nlattr * tb [ TCA_POLICE_MAX + 1 ] ;
2005-04-17 02:20:36 +04:00
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
2008-01-24 07:33:32 +03:00
if ( nla = = NULL )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2008-01-24 07:36:30 +03:00
err = nla_parse_nested ( tb , TCA_POLICE_MAX , nla , police_policy ) ;
2008-01-24 07:33:32 +03:00
if ( err < 0 )
return err ;
2008-01-23 09:11:50 +03:00
if ( tb [ TCA_POLICE_TBF ] = = NULL )
2006-12-01 06:54:05 +03:00
return - EINVAL ;
2008-01-23 09:11:50 +03:00
size = nla_len ( tb [ TCA_POLICE_TBF ] ) ;
2006-12-01 06:54:05 +03:00
if ( size ! = sizeof ( * parm ) & & size ! = sizeof ( struct tc_police_compat ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2008-01-23 09:11:50 +03:00
parm = nla_data ( tb [ TCA_POLICE_TBF ] ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
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 ;
2008-01-23 09:11:50 +03:00
R_tab = qdisc_get_rtab ( & parm - > rate , tb [ TCA_POLICE_RATE ] ) ;
2005-04-17 02:20:36 +04:00
if ( R_tab = = NULL )
goto failure ;
2008-11-26 08:14:06 +03:00
2008-11-27 02:24:32 +03:00
if ( ! est & & ( ret = = ACT_P_CREATED | |
! gen_estimator_active ( & police - > tcf_bstats ,
& police - > tcf_rate_est ) ) ) {
2008-11-26 08:14:06 +03:00
err = - EINVAL ;
goto failure ;
}
2005-04-17 02:20:36 +04:00
if ( parm - > peakrate . rate ) {
P_tab = qdisc_get_rtab ( & parm - > peakrate ,
2008-01-23 09:11:50 +03:00
tb [ TCA_POLICE_PEAKRATE ] ) ;
2008-11-26 08:13:31 +03:00
if ( P_tab = = NULL )
2005-04-17 02:20:36 +04:00
goto failure ;
}
}
2008-11-26 08:13:31 +03:00
2006-08-22 10:54:55 +04:00
spin_lock_bh ( & police - > tcf_lock ) ;
2008-11-26 08:13:31 +03:00
if ( est ) {
err = gen_replace_estimator ( & police - > tcf_bstats ,
& police - > tcf_rate_est ,
& police - > tcf_lock , est ) ;
if ( err )
goto failure_unlock ;
}
/* No failure allowed after this point */
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
}
2008-01-23 09:11:50 +03:00
if ( tb [ TCA_POLICE_RESULT ] )
2008-01-24 07:35:03 +03:00
police - > tcfp_result = nla_get_u32 ( tb [ TCA_POLICE_RESULT ] ) ;
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 ) ;
police - > tcf_action = parm - > action ;
2005-04-17 02:20:36 +04:00
2008-01-23 09:11:50 +03:00
if ( tb [ TCA_POLICE_AVRATE ] )
2008-01-24 07:35:03 +03:00
police - > tcfp_ewma_rate = nla_get_u32 ( tb [ TCA_POLICE_AVRATE ] ) ;
2005-04-17 02:20:36 +04:00
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 ;
2007-03-23 21:29:25 +03:00
police - > tcfp_t_c = psched_get_time ( ) ;
2006-08-22 10:54:55 +04:00
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 ;
2008-11-26 08:13:31 +03:00
failure_unlock :
spin_unlock_bh ( & police - > tcf_lock ) ;
2005-04-17 02:20:36 +04:00
failure :
2008-11-26 08:13:31 +03:00
if ( P_tab )
qdisc_put_rtab ( P_tab ) ;
if ( R_tab )
qdisc_put_rtab ( R_tab ) ;
2005-04-17 02:20:36 +04:00
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 ;
2007-07-15 11:03:05 +04:00
int ret = 0 ;
2005-04-17 02:20:36 +04:00
2007-07-15 11:03:05 +04:00
if ( p ! = NULL ) {
if ( bind )
p - > tcf_bindcnt - - ;
p - > tcf_refcnt - - ;
if ( p - > tcf_refcnt < = 0 & & ! p - > tcf_bindcnt ) {
tcf_police_destroy ( p ) ;
ret = 1 ;
}
}
return ret ;
2005-04-17 02:20:36 +04:00
}
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
2008-07-20 11:08:27 +04:00
police - > tcf_bstats . bytes + = qdisc_pkt_len ( skb ) ;
2006-08-22 10:54:55 +04:00
police - > tcf_bstats . packets + + ;
2005-04-17 02:20:36 +04:00
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
}
2008-07-20 11:08:27 +04:00
if ( qdisc_pkt_len ( skb ) < = police - > tcfp_mtu ) {
2006-08-22 10:54:55 +04:00
if ( police - > tcfp_R_tab = = NULL ) {
spin_unlock ( & police - > tcf_lock ) ;
return police - > tcfp_result ;
2005-04-17 02:20:36 +04:00
}
2007-03-23 21:29:25 +03:00
now = psched_get_time ( ) ;
2007-03-23 21:29:11 +03:00
toks = psched_tdiff_bounded ( now , police - > tcfp_t_c ,
police - > tcfp_burst ) ;
2006-08-22 10:54:55 +04:00
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 ) ;
2008-07-20 11:08:27 +04:00
ptoks - = L2T_P ( police , qdisc_pkt_len ( skb ) ) ;
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 ;
2008-07-20 11:08:27 +04:00
toks - = L2T ( police , qdisc_pkt_len ( skb ) ) ;
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 )
{
2007-04-20 07:29:13 +04:00
unsigned char * b = skb_tail_pointer ( skb ) ;
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 ) ) ;
2008-01-23 09:11:50 +03:00
NLA_PUT ( skb , TCA_POLICE_TBF , sizeof ( opt ) , & opt ) ;
2006-08-22 10:54:55 +04:00
if ( police - > tcfp_result )
2008-01-24 07:34:48 +03:00
NLA_PUT_U32 ( skb , TCA_POLICE_RESULT , police - > tcfp_result ) ;
2006-08-22 10:54:55 +04:00
if ( police - > tcfp_ewma_rate )
2008-01-24 07:34:48 +03:00
NLA_PUT_U32 ( skb , TCA_POLICE_AVRATE , police - > tcfp_ewma_rate ) ;
2005-04-17 02:20:36 +04:00
return skb - > len ;
2008-01-23 09:11:50 +03:00
nla_put_failure :
2007-03-26 10:06:12 +04:00
nlmsg_trim ( skb , b ) ;
2005-04-17 02:20:36 +04:00
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 ) ;