14bba3925SPatrick McHardy /* 24bba3925SPatrick McHardy * net/sched/police.c Input police filter. 34bba3925SPatrick McHardy * 44bba3925SPatrick McHardy * This program is free software; you can redistribute it and/or 54bba3925SPatrick McHardy * modify it under the terms of the GNU General Public License 64bba3925SPatrick McHardy * as published by the Free Software Foundation; either version 74bba3925SPatrick McHardy * 2 of the License, or (at your option) any later version. 84bba3925SPatrick McHardy * 94bba3925SPatrick McHardy * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 104bba3925SPatrick McHardy * J Hadi Salim (action changes) 114bba3925SPatrick McHardy */ 124bba3925SPatrick McHardy 134bba3925SPatrick McHardy #include <asm/uaccess.h> 144bba3925SPatrick McHardy #include <asm/system.h> 154bba3925SPatrick McHardy #include <linux/bitops.h> 164bba3925SPatrick McHardy #include <linux/module.h> 174bba3925SPatrick McHardy #include <linux/types.h> 184bba3925SPatrick McHardy #include <linux/kernel.h> 194bba3925SPatrick McHardy #include <linux/string.h> 204bba3925SPatrick McHardy #include <linux/mm.h> 214bba3925SPatrick McHardy #include <linux/socket.h> 224bba3925SPatrick McHardy #include <linux/sockios.h> 234bba3925SPatrick McHardy #include <linux/in.h> 244bba3925SPatrick McHardy #include <linux/errno.h> 254bba3925SPatrick McHardy #include <linux/interrupt.h> 264bba3925SPatrick McHardy #include <linux/netdevice.h> 274bba3925SPatrick McHardy #include <linux/skbuff.h> 284bba3925SPatrick McHardy #include <linux/module.h> 294bba3925SPatrick McHardy #include <linux/rtnetlink.h> 304bba3925SPatrick McHardy #include <linux/init.h> 314bba3925SPatrick McHardy #include <net/sock.h> 324bba3925SPatrick McHardy #include <net/act_api.h> 33dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h> 344bba3925SPatrick McHardy 35e9ce1cd3SDavid S. Miller #define L2T(p,L) ((p)->tcfp_R_tab->data[(L)>>(p)->tcfp_R_tab->rate.cell_log]) 36e9ce1cd3SDavid S. Miller #define L2T_P(p,L) ((p)->tcfp_P_tab->data[(L)>>(p)->tcfp_P_tab->rate.cell_log]) 374bba3925SPatrick McHardy 38e9ce1cd3SDavid S. Miller #define POL_TAB_MASK 15 39e9ce1cd3SDavid S. Miller static struct tcf_common *tcf_police_ht[POL_TAB_MASK + 1]; 40e9ce1cd3SDavid S. Miller static u32 police_idx_gen; 414bba3925SPatrick McHardy static DEFINE_RWLOCK(police_lock); 424bba3925SPatrick McHardy 43e9ce1cd3SDavid S. Miller static struct tcf_hashinfo police_hash_info = { 44e9ce1cd3SDavid S. Miller .htab = tcf_police_ht, 45e9ce1cd3SDavid S. Miller .hmask = POL_TAB_MASK, 46e9ce1cd3SDavid S. Miller .lock = &police_lock, 47e9ce1cd3SDavid S. Miller }; 48e9ce1cd3SDavid S. Miller 491e9b3d53SPatrick McHardy /* old policer structure from before tc actions */ 501e9b3d53SPatrick McHardy struct tc_police_compat 511e9b3d53SPatrick McHardy { 521e9b3d53SPatrick McHardy u32 index; 531e9b3d53SPatrick McHardy int action; 541e9b3d53SPatrick McHardy u32 limit; 551e9b3d53SPatrick McHardy u32 burst; 561e9b3d53SPatrick McHardy u32 mtu; 571e9b3d53SPatrick McHardy struct tc_ratespec rate; 581e9b3d53SPatrick McHardy struct tc_ratespec peakrate; 591e9b3d53SPatrick McHardy }; 601e9b3d53SPatrick McHardy 614bba3925SPatrick McHardy /* Each policer is serialized by its individual spinlock */ 624bba3925SPatrick McHardy 634bba3925SPatrick McHardy #ifdef CONFIG_NET_CLS_ACT 6483b950c8SJamal Hadi Salim static int tcf_act_police_walker(struct sk_buff *skb, struct netlink_callback *cb, 654bba3925SPatrick McHardy int type, struct tc_action *a) 664bba3925SPatrick McHardy { 67e9ce1cd3SDavid S. Miller struct tcf_common *p; 684bba3925SPatrick McHardy int err = 0, index = -1, i = 0, s_i = 0, n_i = 0; 694bba3925SPatrick McHardy struct rtattr *r; 704bba3925SPatrick McHardy 714bba3925SPatrick McHardy read_lock(&police_lock); 724bba3925SPatrick McHardy 734bba3925SPatrick McHardy s_i = cb->args[0]; 744bba3925SPatrick McHardy 75e9ce1cd3SDavid S. Miller for (i = 0; i < (POL_TAB_MASK + 1); i++) { 76e9ce1cd3SDavid S. Miller p = tcf_police_ht[tcf_hash(i, POL_TAB_MASK)]; 774bba3925SPatrick McHardy 78e9ce1cd3SDavid S. Miller for (; p; p = p->tcfc_next) { 794bba3925SPatrick McHardy index++; 804bba3925SPatrick McHardy if (index < s_i) 814bba3925SPatrick McHardy continue; 824bba3925SPatrick McHardy a->priv = p; 834bba3925SPatrick McHardy a->order = index; 8427a884dcSArnaldo Carvalho de Melo r = (struct rtattr *)skb_tail_pointer(skb); 854bba3925SPatrick McHardy RTA_PUT(skb, a->order, 0, NULL); 864bba3925SPatrick McHardy if (type == RTM_DELACTION) 874bba3925SPatrick McHardy err = tcf_action_dump_1(skb, a, 0, 1); 884bba3925SPatrick McHardy else 894bba3925SPatrick McHardy err = tcf_action_dump_1(skb, a, 0, 0); 904bba3925SPatrick McHardy if (err < 0) { 914bba3925SPatrick McHardy index--; 92dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, r); 934bba3925SPatrick McHardy goto done; 944bba3925SPatrick McHardy } 9527a884dcSArnaldo Carvalho de Melo r->rta_len = skb_tail_pointer(skb) - (u8 *)r; 964bba3925SPatrick McHardy n_i++; 974bba3925SPatrick McHardy } 984bba3925SPatrick McHardy } 994bba3925SPatrick McHardy done: 1004bba3925SPatrick McHardy read_unlock(&police_lock); 1014bba3925SPatrick McHardy if (n_i) 1024bba3925SPatrick McHardy cb->args[0] += n_i; 1034bba3925SPatrick McHardy return n_i; 1044bba3925SPatrick McHardy 1054bba3925SPatrick McHardy rtattr_failure: 106dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, r); 1074bba3925SPatrick McHardy goto done; 1084bba3925SPatrick McHardy } 1094bba3925SPatrick McHardy #endif 1104bba3925SPatrick McHardy 1114bba3925SPatrick McHardy void tcf_police_destroy(struct tcf_police *p) 1124bba3925SPatrick McHardy { 113e9ce1cd3SDavid S. Miller unsigned int h = tcf_hash(p->tcf_index, POL_TAB_MASK); 114e9ce1cd3SDavid S. Miller struct tcf_common **p1p; 1154bba3925SPatrick McHardy 116e9ce1cd3SDavid S. Miller for (p1p = &tcf_police_ht[h]; *p1p; p1p = &(*p1p)->tcfc_next) { 117e9ce1cd3SDavid S. Miller if (*p1p == &p->common) { 1184bba3925SPatrick McHardy write_lock_bh(&police_lock); 119e9ce1cd3SDavid S. Miller *p1p = p->tcf_next; 1204bba3925SPatrick McHardy write_unlock_bh(&police_lock); 1214bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 122e9ce1cd3SDavid S. Miller gen_kill_estimator(&p->tcf_bstats, 123e9ce1cd3SDavid S. Miller &p->tcf_rate_est); 1244bba3925SPatrick McHardy #endif 125e9ce1cd3SDavid S. Miller if (p->tcfp_R_tab) 126e9ce1cd3SDavid S. Miller qdisc_put_rtab(p->tcfp_R_tab); 127e9ce1cd3SDavid S. Miller if (p->tcfp_P_tab) 128e9ce1cd3SDavid S. Miller qdisc_put_rtab(p->tcfp_P_tab); 1294bba3925SPatrick McHardy kfree(p); 1304bba3925SPatrick McHardy return; 1314bba3925SPatrick McHardy } 1324bba3925SPatrick McHardy } 1334bba3925SPatrick McHardy BUG_TRAP(0); 1344bba3925SPatrick McHardy } 1354bba3925SPatrick McHardy 1364bba3925SPatrick McHardy #ifdef CONFIG_NET_CLS_ACT 1374bba3925SPatrick McHardy static int tcf_act_police_locate(struct rtattr *rta, struct rtattr *est, 1384bba3925SPatrick McHardy struct tc_action *a, int ovr, int bind) 1394bba3925SPatrick McHardy { 1404bba3925SPatrick McHardy unsigned h; 1414bba3925SPatrick McHardy int ret = 0, err; 1424bba3925SPatrick McHardy struct rtattr *tb[TCA_POLICE_MAX]; 1434bba3925SPatrick McHardy struct tc_police *parm; 144e9ce1cd3SDavid S. Miller struct tcf_police *police; 1454bba3925SPatrick McHardy struct qdisc_rate_table *R_tab = NULL, *P_tab = NULL; 1461e9b3d53SPatrick McHardy int size; 1474bba3925SPatrick McHardy 1484bba3925SPatrick McHardy if (rta == NULL || rtattr_parse_nested(tb, TCA_POLICE_MAX, rta) < 0) 1494bba3925SPatrick McHardy return -EINVAL; 1504bba3925SPatrick McHardy 1511e9b3d53SPatrick McHardy if (tb[TCA_POLICE_TBF-1] == NULL) 1521e9b3d53SPatrick McHardy return -EINVAL; 1531e9b3d53SPatrick McHardy size = RTA_PAYLOAD(tb[TCA_POLICE_TBF-1]); 1541e9b3d53SPatrick McHardy if (size != sizeof(*parm) && size != sizeof(struct tc_police_compat)) 1554bba3925SPatrick McHardy return -EINVAL; 1564bba3925SPatrick McHardy parm = RTA_DATA(tb[TCA_POLICE_TBF-1]); 1574bba3925SPatrick McHardy 1584bba3925SPatrick McHardy if (tb[TCA_POLICE_RESULT-1] != NULL && 1594bba3925SPatrick McHardy RTA_PAYLOAD(tb[TCA_POLICE_RESULT-1]) != sizeof(u32)) 1604bba3925SPatrick McHardy return -EINVAL; 1614bba3925SPatrick McHardy if (tb[TCA_POLICE_RESULT-1] != NULL && 1624bba3925SPatrick McHardy RTA_PAYLOAD(tb[TCA_POLICE_RESULT-1]) != sizeof(u32)) 1634bba3925SPatrick McHardy return -EINVAL; 1644bba3925SPatrick McHardy 165e9ce1cd3SDavid S. Miller if (parm->index) { 166e9ce1cd3SDavid S. Miller struct tcf_common *pc; 167e9ce1cd3SDavid S. Miller 168e9ce1cd3SDavid S. Miller pc = tcf_hash_lookup(parm->index, &police_hash_info); 169e9ce1cd3SDavid S. Miller if (pc != NULL) { 170e9ce1cd3SDavid S. Miller a->priv = pc; 171e9ce1cd3SDavid S. Miller police = to_police(pc); 1724bba3925SPatrick McHardy if (bind) { 173e9ce1cd3SDavid S. Miller police->tcf_bindcnt += 1; 174e9ce1cd3SDavid S. Miller police->tcf_refcnt += 1; 1754bba3925SPatrick McHardy } 1764bba3925SPatrick McHardy if (ovr) 1774bba3925SPatrick McHardy goto override; 1784bba3925SPatrick McHardy return ret; 1794bba3925SPatrick McHardy } 180e9ce1cd3SDavid S. Miller } 1814bba3925SPatrick McHardy 182e9ce1cd3SDavid S. Miller police = kzalloc(sizeof(*police), GFP_KERNEL); 183e9ce1cd3SDavid S. Miller if (police == NULL) 1844bba3925SPatrick McHardy return -ENOMEM; 1854bba3925SPatrick McHardy ret = ACT_P_CREATED; 186e9ce1cd3SDavid S. Miller police->tcf_refcnt = 1; 187e9ce1cd3SDavid S. Miller spin_lock_init(&police->tcf_lock); 188e9ce1cd3SDavid S. Miller police->tcf_stats_lock = &police->tcf_lock; 1894bba3925SPatrick McHardy if (bind) 190e9ce1cd3SDavid S. Miller police->tcf_bindcnt = 1; 1914bba3925SPatrick McHardy override: 1924bba3925SPatrick McHardy if (parm->rate.rate) { 1934bba3925SPatrick McHardy err = -ENOMEM; 1944bba3925SPatrick McHardy R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE-1]); 1954bba3925SPatrick McHardy if (R_tab == NULL) 1964bba3925SPatrick McHardy goto failure; 1974bba3925SPatrick McHardy if (parm->peakrate.rate) { 1984bba3925SPatrick McHardy P_tab = qdisc_get_rtab(&parm->peakrate, 1994bba3925SPatrick McHardy tb[TCA_POLICE_PEAKRATE-1]); 200e9ce1cd3SDavid S. Miller if (P_tab == NULL) { 2014bba3925SPatrick McHardy qdisc_put_rtab(R_tab); 2024bba3925SPatrick McHardy goto failure; 2034bba3925SPatrick McHardy } 2044bba3925SPatrick McHardy } 2054bba3925SPatrick McHardy } 2064bba3925SPatrick McHardy /* No failure allowed after this point */ 207e9ce1cd3SDavid S. Miller spin_lock_bh(&police->tcf_lock); 2084bba3925SPatrick McHardy if (R_tab != NULL) { 209e9ce1cd3SDavid S. Miller qdisc_put_rtab(police->tcfp_R_tab); 210e9ce1cd3SDavid S. Miller police->tcfp_R_tab = R_tab; 2114bba3925SPatrick McHardy } 2124bba3925SPatrick McHardy if (P_tab != NULL) { 213e9ce1cd3SDavid S. Miller qdisc_put_rtab(police->tcfp_P_tab); 214e9ce1cd3SDavid S. Miller police->tcfp_P_tab = P_tab; 2154bba3925SPatrick McHardy } 2164bba3925SPatrick McHardy 2174bba3925SPatrick McHardy if (tb[TCA_POLICE_RESULT-1]) 218e9ce1cd3SDavid S. Miller police->tcfp_result = *(u32*)RTA_DATA(tb[TCA_POLICE_RESULT-1]); 219e9ce1cd3SDavid S. Miller police->tcfp_toks = police->tcfp_burst = parm->burst; 220e9ce1cd3SDavid S. Miller police->tcfp_mtu = parm->mtu; 221e9ce1cd3SDavid S. Miller if (police->tcfp_mtu == 0) { 222e9ce1cd3SDavid S. Miller police->tcfp_mtu = ~0; 223e9ce1cd3SDavid S. Miller if (police->tcfp_R_tab) 224e9ce1cd3SDavid S. Miller police->tcfp_mtu = 255<<police->tcfp_R_tab->rate.cell_log; 2254bba3925SPatrick McHardy } 226e9ce1cd3SDavid S. Miller if (police->tcfp_P_tab) 227e9ce1cd3SDavid S. Miller police->tcfp_ptoks = L2T_P(police, police->tcfp_mtu); 228e9ce1cd3SDavid S. Miller police->tcf_action = parm->action; 2294bba3925SPatrick McHardy 2304bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 2314bba3925SPatrick McHardy if (tb[TCA_POLICE_AVRATE-1]) 232e9ce1cd3SDavid S. Miller police->tcfp_ewma_rate = 233e9ce1cd3SDavid S. Miller *(u32*)RTA_DATA(tb[TCA_POLICE_AVRATE-1]); 2344bba3925SPatrick McHardy if (est) 235e9ce1cd3SDavid S. Miller gen_replace_estimator(&police->tcf_bstats, 236e9ce1cd3SDavid S. Miller &police->tcf_rate_est, 237e9ce1cd3SDavid S. Miller police->tcf_stats_lock, est); 2384bba3925SPatrick McHardy #endif 2394bba3925SPatrick McHardy 240e9ce1cd3SDavid S. Miller spin_unlock_bh(&police->tcf_lock); 2414bba3925SPatrick McHardy if (ret != ACT_P_CREATED) 2424bba3925SPatrick McHardy return ret; 2434bba3925SPatrick McHardy 244e9ce1cd3SDavid S. Miller PSCHED_GET_TIME(police->tcfp_t_c); 245e9ce1cd3SDavid S. Miller police->tcf_index = parm->index ? parm->index : 246e9ce1cd3SDavid S. Miller tcf_hash_new_index(&police_idx_gen, &police_hash_info); 247e9ce1cd3SDavid S. Miller h = tcf_hash(police->tcf_index, POL_TAB_MASK); 2484bba3925SPatrick McHardy write_lock_bh(&police_lock); 249e9ce1cd3SDavid S. Miller police->tcf_next = tcf_police_ht[h]; 250e9ce1cd3SDavid S. Miller tcf_police_ht[h] = &police->common; 2514bba3925SPatrick McHardy write_unlock_bh(&police_lock); 2524bba3925SPatrick McHardy 253e9ce1cd3SDavid S. Miller a->priv = police; 2544bba3925SPatrick McHardy return ret; 2554bba3925SPatrick McHardy 2564bba3925SPatrick McHardy failure: 2574bba3925SPatrick McHardy if (ret == ACT_P_CREATED) 258e9ce1cd3SDavid S. Miller kfree(police); 2594bba3925SPatrick McHardy return err; 2604bba3925SPatrick McHardy } 2614bba3925SPatrick McHardy 2624bba3925SPatrick McHardy static int tcf_act_police_cleanup(struct tc_action *a, int bind) 2634bba3925SPatrick McHardy { 264e9ce1cd3SDavid S. Miller struct tcf_police *p = a->priv; 2654bba3925SPatrick McHardy 2664bba3925SPatrick McHardy if (p != NULL) 2674bba3925SPatrick McHardy return tcf_police_release(p, bind); 2684bba3925SPatrick McHardy return 0; 2694bba3925SPatrick McHardy } 2704bba3925SPatrick McHardy 2714bba3925SPatrick McHardy static int tcf_act_police(struct sk_buff *skb, struct tc_action *a, 2724bba3925SPatrick McHardy struct tcf_result *res) 2734bba3925SPatrick McHardy { 274e9ce1cd3SDavid S. Miller struct tcf_police *police = a->priv; 2754bba3925SPatrick McHardy psched_time_t now; 2764bba3925SPatrick McHardy long toks; 2774bba3925SPatrick McHardy long ptoks = 0; 2784bba3925SPatrick McHardy 279e9ce1cd3SDavid S. Miller spin_lock(&police->tcf_lock); 2804bba3925SPatrick McHardy 281e9ce1cd3SDavid S. Miller police->tcf_bstats.bytes += skb->len; 282e9ce1cd3SDavid S. Miller police->tcf_bstats.packets++; 2834bba3925SPatrick McHardy 2844bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 285e9ce1cd3SDavid S. Miller if (police->tcfp_ewma_rate && 286e9ce1cd3SDavid S. Miller police->tcf_rate_est.bps >= police->tcfp_ewma_rate) { 287e9ce1cd3SDavid S. Miller police->tcf_qstats.overlimits++; 288e9ce1cd3SDavid S. Miller spin_unlock(&police->tcf_lock); 289e9ce1cd3SDavid S. Miller return police->tcf_action; 2904bba3925SPatrick McHardy } 2914bba3925SPatrick McHardy #endif 2924bba3925SPatrick McHardy 293e9ce1cd3SDavid S. Miller if (skb->len <= police->tcfp_mtu) { 294e9ce1cd3SDavid S. Miller if (police->tcfp_R_tab == NULL) { 295e9ce1cd3SDavid S. Miller spin_unlock(&police->tcf_lock); 296e9ce1cd3SDavid S. Miller return police->tcfp_result; 2974bba3925SPatrick McHardy } 2984bba3925SPatrick McHardy 2994bba3925SPatrick McHardy PSCHED_GET_TIME(now); 3004bba3925SPatrick McHardy 301*03cc45c0SPatrick McHardy toks = psched_tdiff_bounded(now, police->tcfp_t_c, 302e9ce1cd3SDavid S. Miller police->tcfp_burst); 303e9ce1cd3SDavid S. Miller if (police->tcfp_P_tab) { 304e9ce1cd3SDavid S. Miller ptoks = toks + police->tcfp_ptoks; 305e9ce1cd3SDavid S. Miller if (ptoks > (long)L2T_P(police, police->tcfp_mtu)) 306e9ce1cd3SDavid S. Miller ptoks = (long)L2T_P(police, police->tcfp_mtu); 307e9ce1cd3SDavid S. Miller ptoks -= L2T_P(police, skb->len); 3084bba3925SPatrick McHardy } 309e9ce1cd3SDavid S. Miller toks += police->tcfp_toks; 310e9ce1cd3SDavid S. Miller if (toks > (long)police->tcfp_burst) 311e9ce1cd3SDavid S. Miller toks = police->tcfp_burst; 312e9ce1cd3SDavid S. Miller toks -= L2T(police, skb->len); 3134bba3925SPatrick McHardy if ((toks|ptoks) >= 0) { 314e9ce1cd3SDavid S. Miller police->tcfp_t_c = now; 315e9ce1cd3SDavid S. Miller police->tcfp_toks = toks; 316e9ce1cd3SDavid S. Miller police->tcfp_ptoks = ptoks; 317e9ce1cd3SDavid S. Miller spin_unlock(&police->tcf_lock); 318e9ce1cd3SDavid S. Miller return police->tcfp_result; 3194bba3925SPatrick McHardy } 3204bba3925SPatrick McHardy } 3214bba3925SPatrick McHardy 322e9ce1cd3SDavid S. Miller police->tcf_qstats.overlimits++; 323e9ce1cd3SDavid S. Miller spin_unlock(&police->tcf_lock); 324e9ce1cd3SDavid S. Miller return police->tcf_action; 3254bba3925SPatrick McHardy } 3264bba3925SPatrick McHardy 3274bba3925SPatrick McHardy static int 3284bba3925SPatrick McHardy tcf_act_police_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) 3294bba3925SPatrick McHardy { 33027a884dcSArnaldo Carvalho de Melo unsigned char *b = skb_tail_pointer(skb); 331e9ce1cd3SDavid S. Miller struct tcf_police *police = a->priv; 3324bba3925SPatrick McHardy struct tc_police opt; 3334bba3925SPatrick McHardy 334e9ce1cd3SDavid S. Miller opt.index = police->tcf_index; 335e9ce1cd3SDavid S. Miller opt.action = police->tcf_action; 336e9ce1cd3SDavid S. Miller opt.mtu = police->tcfp_mtu; 337e9ce1cd3SDavid S. Miller opt.burst = police->tcfp_burst; 338e9ce1cd3SDavid S. Miller opt.refcnt = police->tcf_refcnt - ref; 339e9ce1cd3SDavid S. Miller opt.bindcnt = police->tcf_bindcnt - bind; 340e9ce1cd3SDavid S. Miller if (police->tcfp_R_tab) 341e9ce1cd3SDavid S. Miller opt.rate = police->tcfp_R_tab->rate; 3424bba3925SPatrick McHardy else 3434bba3925SPatrick McHardy memset(&opt.rate, 0, sizeof(opt.rate)); 344e9ce1cd3SDavid S. Miller if (police->tcfp_P_tab) 345e9ce1cd3SDavid S. Miller opt.peakrate = police->tcfp_P_tab->rate; 3464bba3925SPatrick McHardy else 3474bba3925SPatrick McHardy memset(&opt.peakrate, 0, sizeof(opt.peakrate)); 3484bba3925SPatrick McHardy RTA_PUT(skb, TCA_POLICE_TBF, sizeof(opt), &opt); 349e9ce1cd3SDavid S. Miller if (police->tcfp_result) 350e9ce1cd3SDavid S. Miller RTA_PUT(skb, TCA_POLICE_RESULT, sizeof(int), 351e9ce1cd3SDavid S. Miller &police->tcfp_result); 3524bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 353e9ce1cd3SDavid S. Miller if (police->tcfp_ewma_rate) 354e9ce1cd3SDavid S. Miller RTA_PUT(skb, TCA_POLICE_AVRATE, 4, &police->tcfp_ewma_rate); 3554bba3925SPatrick McHardy #endif 3564bba3925SPatrick McHardy return skb->len; 3574bba3925SPatrick McHardy 3584bba3925SPatrick McHardy rtattr_failure: 359dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, b); 3604bba3925SPatrick McHardy return -1; 3614bba3925SPatrick McHardy } 3624bba3925SPatrick McHardy 3634bba3925SPatrick McHardy MODULE_AUTHOR("Alexey Kuznetsov"); 3644bba3925SPatrick McHardy MODULE_DESCRIPTION("Policing actions"); 3654bba3925SPatrick McHardy MODULE_LICENSE("GPL"); 3664bba3925SPatrick McHardy 3674bba3925SPatrick McHardy static struct tc_action_ops act_police_ops = { 3684bba3925SPatrick McHardy .kind = "police", 369e9ce1cd3SDavid S. Miller .hinfo = &police_hash_info, 3704bba3925SPatrick McHardy .type = TCA_ID_POLICE, 3714bba3925SPatrick McHardy .capab = TCA_CAP_NONE, 3724bba3925SPatrick McHardy .owner = THIS_MODULE, 3734bba3925SPatrick McHardy .act = tcf_act_police, 3744bba3925SPatrick McHardy .dump = tcf_act_police_dump, 3754bba3925SPatrick McHardy .cleanup = tcf_act_police_cleanup, 376e9ce1cd3SDavid S. Miller .lookup = tcf_hash_search, 3774bba3925SPatrick McHardy .init = tcf_act_police_locate, 37883b950c8SJamal Hadi Salim .walk = tcf_act_police_walker 3794bba3925SPatrick McHardy }; 3804bba3925SPatrick McHardy 3814bba3925SPatrick McHardy static int __init 3824bba3925SPatrick McHardy police_init_module(void) 3834bba3925SPatrick McHardy { 3844bba3925SPatrick McHardy return tcf_register_action(&act_police_ops); 3854bba3925SPatrick McHardy } 3864bba3925SPatrick McHardy 3874bba3925SPatrick McHardy static void __exit 3884bba3925SPatrick McHardy police_cleanup_module(void) 3894bba3925SPatrick McHardy { 3904bba3925SPatrick McHardy tcf_unregister_action(&act_police_ops); 3914bba3925SPatrick McHardy } 3924bba3925SPatrick McHardy 3934bba3925SPatrick McHardy module_init(police_init_module); 3944bba3925SPatrick McHardy module_exit(police_cleanup_module); 3954bba3925SPatrick McHardy 3964bba3925SPatrick McHardy #else /* CONFIG_NET_CLS_ACT */ 3974bba3925SPatrick McHardy 398e9ce1cd3SDavid S. Miller static struct tcf_common *tcf_police_lookup(u32 index) 399e9ce1cd3SDavid S. Miller { 400e9ce1cd3SDavid S. Miller struct tcf_hashinfo *hinfo = &police_hash_info; 401e9ce1cd3SDavid S. Miller struct tcf_common *p; 402e9ce1cd3SDavid S. Miller 403e9ce1cd3SDavid S. Miller read_lock(hinfo->lock); 404e9ce1cd3SDavid S. Miller for (p = hinfo->htab[tcf_hash(index, hinfo->hmask)]; p; 405e9ce1cd3SDavid S. Miller p = p->tcfc_next) { 406e9ce1cd3SDavid S. Miller if (p->tcfc_index == index) 407e9ce1cd3SDavid S. Miller break; 408e9ce1cd3SDavid S. Miller } 409e9ce1cd3SDavid S. Miller read_unlock(hinfo->lock); 410e9ce1cd3SDavid S. Miller 411e9ce1cd3SDavid S. Miller return p; 412e9ce1cd3SDavid S. Miller } 413e9ce1cd3SDavid S. Miller 414e9ce1cd3SDavid S. Miller static u32 tcf_police_new_index(void) 415e9ce1cd3SDavid S. Miller { 416e9ce1cd3SDavid S. Miller u32 *idx_gen = &police_idx_gen; 417e9ce1cd3SDavid S. Miller u32 val = *idx_gen; 418e9ce1cd3SDavid S. Miller 419e9ce1cd3SDavid S. Miller do { 420e9ce1cd3SDavid S. Miller if (++val == 0) 421e9ce1cd3SDavid S. Miller val = 1; 422e9ce1cd3SDavid S. Miller } while (tcf_police_lookup(val)); 423e9ce1cd3SDavid S. Miller 424e9ce1cd3SDavid S. Miller return (*idx_gen = val); 425e9ce1cd3SDavid S. Miller } 426e9ce1cd3SDavid S. Miller 4274bba3925SPatrick McHardy struct tcf_police *tcf_police_locate(struct rtattr *rta, struct rtattr *est) 4284bba3925SPatrick McHardy { 429e9ce1cd3SDavid S. Miller unsigned int h; 430e9ce1cd3SDavid S. Miller struct tcf_police *police; 4314bba3925SPatrick McHardy struct rtattr *tb[TCA_POLICE_MAX]; 4324bba3925SPatrick McHardy struct tc_police *parm; 4331e9b3d53SPatrick McHardy int size; 4344bba3925SPatrick McHardy 4354bba3925SPatrick McHardy if (rtattr_parse_nested(tb, TCA_POLICE_MAX, rta) < 0) 4364bba3925SPatrick McHardy return NULL; 4374bba3925SPatrick McHardy 4381e9b3d53SPatrick McHardy if (tb[TCA_POLICE_TBF-1] == NULL) 4391e9b3d53SPatrick McHardy return NULL; 4401e9b3d53SPatrick McHardy size = RTA_PAYLOAD(tb[TCA_POLICE_TBF-1]); 4411e9b3d53SPatrick McHardy if (size != sizeof(*parm) && size != sizeof(struct tc_police_compat)) 4424bba3925SPatrick McHardy return NULL; 4434bba3925SPatrick McHardy 4444bba3925SPatrick McHardy parm = RTA_DATA(tb[TCA_POLICE_TBF-1]); 4454bba3925SPatrick McHardy 446e9ce1cd3SDavid S. Miller if (parm->index) { 447e9ce1cd3SDavid S. Miller struct tcf_common *pc; 4484bba3925SPatrick McHardy 449e9ce1cd3SDavid S. Miller pc = tcf_police_lookup(parm->index); 450e9ce1cd3SDavid S. Miller if (pc) { 451e9ce1cd3SDavid S. Miller police = to_police(pc); 452e9ce1cd3SDavid S. Miller police->tcf_refcnt++; 453e9ce1cd3SDavid S. Miller return police; 454e9ce1cd3SDavid S. Miller } 455e9ce1cd3SDavid S. Miller } 456e9ce1cd3SDavid S. Miller police = kzalloc(sizeof(*police), GFP_KERNEL); 457e9ce1cd3SDavid S. Miller if (unlikely(!police)) 4584bba3925SPatrick McHardy return NULL; 4594bba3925SPatrick McHardy 460e9ce1cd3SDavid S. Miller police->tcf_refcnt = 1; 461e9ce1cd3SDavid S. Miller spin_lock_init(&police->tcf_lock); 462e9ce1cd3SDavid S. Miller police->tcf_stats_lock = &police->tcf_lock; 4634bba3925SPatrick McHardy if (parm->rate.rate) { 464e9ce1cd3SDavid S. Miller police->tcfp_R_tab = 465e9ce1cd3SDavid S. Miller qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE-1]); 466e9ce1cd3SDavid S. Miller if (police->tcfp_R_tab == NULL) 4674bba3925SPatrick McHardy goto failure; 4684bba3925SPatrick McHardy if (parm->peakrate.rate) { 469e9ce1cd3SDavid S. Miller police->tcfp_P_tab = 470e9ce1cd3SDavid S. Miller qdisc_get_rtab(&parm->peakrate, 4714bba3925SPatrick McHardy tb[TCA_POLICE_PEAKRATE-1]); 472e9ce1cd3SDavid S. Miller if (police->tcfp_P_tab == NULL) 4734bba3925SPatrick McHardy goto failure; 4744bba3925SPatrick McHardy } 4754bba3925SPatrick McHardy } 4764bba3925SPatrick McHardy if (tb[TCA_POLICE_RESULT-1]) { 4774bba3925SPatrick McHardy if (RTA_PAYLOAD(tb[TCA_POLICE_RESULT-1]) != sizeof(u32)) 4784bba3925SPatrick McHardy goto failure; 479e9ce1cd3SDavid S. Miller police->tcfp_result = *(u32*)RTA_DATA(tb[TCA_POLICE_RESULT-1]); 4804bba3925SPatrick McHardy } 4814bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 4824bba3925SPatrick McHardy if (tb[TCA_POLICE_AVRATE-1]) { 4834bba3925SPatrick McHardy if (RTA_PAYLOAD(tb[TCA_POLICE_AVRATE-1]) != sizeof(u32)) 4844bba3925SPatrick McHardy goto failure; 485e9ce1cd3SDavid S. Miller police->tcfp_ewma_rate = 486e9ce1cd3SDavid S. Miller *(u32*)RTA_DATA(tb[TCA_POLICE_AVRATE-1]); 4874bba3925SPatrick McHardy } 4884bba3925SPatrick McHardy #endif 489e9ce1cd3SDavid S. Miller police->tcfp_toks = police->tcfp_burst = parm->burst; 490e9ce1cd3SDavid S. Miller police->tcfp_mtu = parm->mtu; 491e9ce1cd3SDavid S. Miller if (police->tcfp_mtu == 0) { 492e9ce1cd3SDavid S. Miller police->tcfp_mtu = ~0; 493e9ce1cd3SDavid S. Miller if (police->tcfp_R_tab) 494e9ce1cd3SDavid S. Miller police->tcfp_mtu = 255<<police->tcfp_R_tab->rate.cell_log; 4954bba3925SPatrick McHardy } 496e9ce1cd3SDavid S. Miller if (police->tcfp_P_tab) 497e9ce1cd3SDavid S. Miller police->tcfp_ptoks = L2T_P(police, police->tcfp_mtu); 498e9ce1cd3SDavid S. Miller PSCHED_GET_TIME(police->tcfp_t_c); 499e9ce1cd3SDavid S. Miller police->tcf_index = parm->index ? parm->index : 500e9ce1cd3SDavid S. Miller tcf_police_new_index(); 501e9ce1cd3SDavid S. Miller police->tcf_action = parm->action; 5024bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 5034bba3925SPatrick McHardy if (est) 504e9ce1cd3SDavid S. Miller gen_new_estimator(&police->tcf_bstats, &police->tcf_rate_est, 505e9ce1cd3SDavid S. Miller police->tcf_stats_lock, est); 5064bba3925SPatrick McHardy #endif 507e9ce1cd3SDavid S. Miller h = tcf_hash(police->tcf_index, POL_TAB_MASK); 5084bba3925SPatrick McHardy write_lock_bh(&police_lock); 509e9ce1cd3SDavid S. Miller police->tcf_next = tcf_police_ht[h]; 510e9ce1cd3SDavid S. Miller tcf_police_ht[h] = &police->common; 5114bba3925SPatrick McHardy write_unlock_bh(&police_lock); 512e9ce1cd3SDavid S. Miller return police; 5134bba3925SPatrick McHardy 5144bba3925SPatrick McHardy failure: 515e9ce1cd3SDavid S. Miller if (police->tcfp_R_tab) 516e9ce1cd3SDavid S. Miller qdisc_put_rtab(police->tcfp_R_tab); 517e9ce1cd3SDavid S. Miller kfree(police); 5184bba3925SPatrick McHardy return NULL; 5194bba3925SPatrick McHardy } 5204bba3925SPatrick McHardy 521e9ce1cd3SDavid S. Miller int tcf_police(struct sk_buff *skb, struct tcf_police *police) 5224bba3925SPatrick McHardy { 5234bba3925SPatrick McHardy psched_time_t now; 5244bba3925SPatrick McHardy long toks; 5254bba3925SPatrick McHardy long ptoks = 0; 5264bba3925SPatrick McHardy 527e9ce1cd3SDavid S. Miller spin_lock(&police->tcf_lock); 5284bba3925SPatrick McHardy 529e9ce1cd3SDavid S. Miller police->tcf_bstats.bytes += skb->len; 530e9ce1cd3SDavid S. Miller police->tcf_bstats.packets++; 5314bba3925SPatrick McHardy 5324bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 533e9ce1cd3SDavid S. Miller if (police->tcfp_ewma_rate && 534e9ce1cd3SDavid S. Miller police->tcf_rate_est.bps >= police->tcfp_ewma_rate) { 535e9ce1cd3SDavid S. Miller police->tcf_qstats.overlimits++; 536e9ce1cd3SDavid S. Miller spin_unlock(&police->tcf_lock); 537e9ce1cd3SDavid S. Miller return police->tcf_action; 5384bba3925SPatrick McHardy } 5394bba3925SPatrick McHardy #endif 540e9ce1cd3SDavid S. Miller if (skb->len <= police->tcfp_mtu) { 541e9ce1cd3SDavid S. Miller if (police->tcfp_R_tab == NULL) { 542e9ce1cd3SDavid S. Miller spin_unlock(&police->tcf_lock); 543e9ce1cd3SDavid S. Miller return police->tcfp_result; 5444bba3925SPatrick McHardy } 5454bba3925SPatrick McHardy 5464bba3925SPatrick McHardy PSCHED_GET_TIME(now); 547*03cc45c0SPatrick McHardy toks = psched_tdiff_bounded(now, police->tcfp_t_c, 548e9ce1cd3SDavid S. Miller police->tcfp_burst); 549e9ce1cd3SDavid S. Miller if (police->tcfp_P_tab) { 550e9ce1cd3SDavid S. Miller ptoks = toks + police->tcfp_ptoks; 551e9ce1cd3SDavid S. Miller if (ptoks > (long)L2T_P(police, police->tcfp_mtu)) 552e9ce1cd3SDavid S. Miller ptoks = (long)L2T_P(police, police->tcfp_mtu); 553e9ce1cd3SDavid S. Miller ptoks -= L2T_P(police, skb->len); 5544bba3925SPatrick McHardy } 555e9ce1cd3SDavid S. Miller toks += police->tcfp_toks; 556e9ce1cd3SDavid S. Miller if (toks > (long)police->tcfp_burst) 557e9ce1cd3SDavid S. Miller toks = police->tcfp_burst; 558e9ce1cd3SDavid S. Miller toks -= L2T(police, skb->len); 5594bba3925SPatrick McHardy if ((toks|ptoks) >= 0) { 560e9ce1cd3SDavid S. Miller police->tcfp_t_c = now; 561e9ce1cd3SDavid S. Miller police->tcfp_toks = toks; 562e9ce1cd3SDavid S. Miller police->tcfp_ptoks = ptoks; 563e9ce1cd3SDavid S. Miller spin_unlock(&police->tcf_lock); 564e9ce1cd3SDavid S. Miller return police->tcfp_result; 5654bba3925SPatrick McHardy } 5664bba3925SPatrick McHardy } 5674bba3925SPatrick McHardy 568e9ce1cd3SDavid S. Miller police->tcf_qstats.overlimits++; 569e9ce1cd3SDavid S. Miller spin_unlock(&police->tcf_lock); 570e9ce1cd3SDavid S. Miller return police->tcf_action; 5714bba3925SPatrick McHardy } 5724bba3925SPatrick McHardy EXPORT_SYMBOL(tcf_police); 5734bba3925SPatrick McHardy 574e9ce1cd3SDavid S. Miller int tcf_police_dump(struct sk_buff *skb, struct tcf_police *police) 5754bba3925SPatrick McHardy { 57627a884dcSArnaldo Carvalho de Melo unsigned char *b = skb_tail_pointer(skb); 5774bba3925SPatrick McHardy struct tc_police opt; 5784bba3925SPatrick McHardy 579e9ce1cd3SDavid S. Miller opt.index = police->tcf_index; 580e9ce1cd3SDavid S. Miller opt.action = police->tcf_action; 581e9ce1cd3SDavid S. Miller opt.mtu = police->tcfp_mtu; 582e9ce1cd3SDavid S. Miller opt.burst = police->tcfp_burst; 583e9ce1cd3SDavid S. Miller if (police->tcfp_R_tab) 584e9ce1cd3SDavid S. Miller opt.rate = police->tcfp_R_tab->rate; 5854bba3925SPatrick McHardy else 5864bba3925SPatrick McHardy memset(&opt.rate, 0, sizeof(opt.rate)); 587e9ce1cd3SDavid S. Miller if (police->tcfp_P_tab) 588e9ce1cd3SDavid S. Miller opt.peakrate = police->tcfp_P_tab->rate; 5894bba3925SPatrick McHardy else 5904bba3925SPatrick McHardy memset(&opt.peakrate, 0, sizeof(opt.peakrate)); 5914bba3925SPatrick McHardy RTA_PUT(skb, TCA_POLICE_TBF, sizeof(opt), &opt); 592e9ce1cd3SDavid S. Miller if (police->tcfp_result) 593e9ce1cd3SDavid S. Miller RTA_PUT(skb, TCA_POLICE_RESULT, sizeof(int), 594e9ce1cd3SDavid S. Miller &police->tcfp_result); 5954bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 596e9ce1cd3SDavid S. Miller if (police->tcfp_ewma_rate) 597e9ce1cd3SDavid S. Miller RTA_PUT(skb, TCA_POLICE_AVRATE, 4, &police->tcfp_ewma_rate); 5984bba3925SPatrick McHardy #endif 5994bba3925SPatrick McHardy return skb->len; 6004bba3925SPatrick McHardy 6014bba3925SPatrick McHardy rtattr_failure: 602dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, b); 6034bba3925SPatrick McHardy return -1; 6044bba3925SPatrick McHardy } 6054bba3925SPatrick McHardy 606e9ce1cd3SDavid S. Miller int tcf_police_dump_stats(struct sk_buff *skb, struct tcf_police *police) 6074bba3925SPatrick McHardy { 6084bba3925SPatrick McHardy struct gnet_dump d; 6094bba3925SPatrick McHardy 6104bba3925SPatrick McHardy if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, 611e9ce1cd3SDavid S. Miller TCA_XSTATS, police->tcf_stats_lock, 612e9ce1cd3SDavid S. Miller &d) < 0) 6134bba3925SPatrick McHardy goto errout; 6144bba3925SPatrick McHardy 615e9ce1cd3SDavid S. Miller if (gnet_stats_copy_basic(&d, &police->tcf_bstats) < 0 || 6164bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 617e9ce1cd3SDavid S. Miller gnet_stats_copy_rate_est(&d, &police->tcf_rate_est) < 0 || 6184bba3925SPatrick McHardy #endif 619e9ce1cd3SDavid S. Miller gnet_stats_copy_queue(&d, &police->tcf_qstats) < 0) 6204bba3925SPatrick McHardy goto errout; 6214bba3925SPatrick McHardy 6224bba3925SPatrick McHardy if (gnet_stats_finish_copy(&d) < 0) 6234bba3925SPatrick McHardy goto errout; 6244bba3925SPatrick McHardy 6254bba3925SPatrick McHardy return 0; 6264bba3925SPatrick McHardy 6274bba3925SPatrick McHardy errout: 6284bba3925SPatrick McHardy return -1; 6294bba3925SPatrick McHardy } 6304bba3925SPatrick McHardy 6314bba3925SPatrick McHardy #endif /* CONFIG_NET_CLS_ACT */ 632