linux/net/sched/act_police.c
<<
>>
Prefs
   1/*
   2 * net/sched/police.c   Input police filter.
   3 *
   4 *              This program is free software; you can redistribute it and/or
   5 *              modify it under the terms of the GNU General Public License
   6 *              as published by the Free Software Foundation; either version
   7 *              2 of the License, or (at your option) any later version.
   8 *
   9 * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  10 *              J Hadi Salim (action changes)
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/types.h>
  15#include <linux/kernel.h>
  16#include <linux/string.h>
  17#include <linux/errno.h>
  18#include <linux/skbuff.h>
  19#include <linux/rtnetlink.h>
  20#include <linux/init.h>
  21#include <linux/slab.h>
  22#include <net/act_api.h>
  23#include <net/netlink.h>
  24
  25#define L2T(p, L)   qdisc_l2t((p)->tcfp_R_tab, L)
  26#define L2T_P(p, L) qdisc_l2t((p)->tcfp_P_tab, L)
  27
  28#define POL_TAB_MASK     15
  29static struct tcf_common *tcf_police_ht[POL_TAB_MASK + 1];
  30static u32 police_idx_gen;
  31static DEFINE_RWLOCK(police_lock);
  32
  33static struct tcf_hashinfo police_hash_info = {
  34        .htab   =       tcf_police_ht,
  35        .hmask  =       POL_TAB_MASK,
  36        .lock   =       &police_lock,
  37};
  38
  39/* old policer structure from before tc actions */
  40struct tc_police_compat {
  41        u32                     index;
  42        int                     action;
  43        u32                     limit;
  44        u32                     burst;
  45        u32                     mtu;
  46        struct tc_ratespec      rate;
  47        struct tc_ratespec      peakrate;
  48};
  49
  50/* Each policer is serialized by its individual spinlock */
  51
  52static int tcf_act_police_walker(struct sk_buff *skb, struct netlink_callback *cb,
  53                              int type, struct tc_action *a)
  54{
  55        struct tcf_common *p;
  56        int err = 0, index = -1, i = 0, s_i = 0, n_i = 0;
  57        struct nlattr *nest;
  58
  59        read_lock_bh(&police_lock);
  60
  61        s_i = cb->args[0];
  62
  63        for (i = 0; i < (POL_TAB_MASK + 1); i++) {
  64                p = tcf_police_ht[tcf_hash(i, POL_TAB_MASK)];
  65
  66                for (; p; p = p->tcfc_next) {
  67                        index++;
  68                        if (index < s_i)
  69                                continue;
  70                        a->priv = p;
  71                        a->order = index;
  72                        nest = nla_nest_start(skb, a->order);
  73                        if (nest == NULL)
  74                                goto nla_put_failure;
  75                        if (type == RTM_DELACTION)
  76                                err = tcf_action_dump_1(skb, a, 0, 1);
  77                        else
  78                                err = tcf_action_dump_1(skb, a, 0, 0);
  79                        if (err < 0) {
  80                                index--;
  81                                nla_nest_cancel(skb, nest);
  82                                goto done;
  83                        }
  84                        nla_nest_end(skb, nest);
  85                        n_i++;
  86                }
  87        }
  88done:
  89        read_unlock_bh(&police_lock);
  90        if (n_i)
  91                cb->args[0] += n_i;
  92        return n_i;
  93
  94nla_put_failure:
  95        nla_nest_cancel(skb, nest);
  96        goto done;
  97}
  98
  99static void tcf_police_destroy(struct tcf_police *p)
 100{
 101        unsigned int h = tcf_hash(p->tcf_index, POL_TAB_MASK);
 102        struct tcf_common **p1p;
 103
 104        for (p1p = &tcf_police_ht[h]; *p1p; p1p = &(*p1p)->tcfc_next) {
 105                if (*p1p == &p->common) {
 106                        write_lock_bh(&police_lock);
 107                        *p1p = p->tcf_next;
 108                        write_unlock_bh(&police_lock);
 109                        gen_kill_estimator(&p->tcf_bstats,
 110                                           &p->tcf_rate_est);
 111                        if (p->tcfp_R_tab)
 112                                qdisc_put_rtab(p->tcfp_R_tab);
 113                        if (p->tcfp_P_tab)
 114                                qdisc_put_rtab(p->tcfp_P_tab);
 115                        /*
 116                         * gen_estimator est_timer() might access p->tcf_lock
 117                         * or bstats, wait a RCU grace period before freeing p
 118                         */
 119                        kfree_rcu(p, tcf_rcu);
 120                        return;
 121                }
 122        }
 123        WARN_ON(1);
 124}
 125
 126static const struct nla_policy police_policy[TCA_POLICE_MAX + 1] = {
 127        [TCA_POLICE_RATE]       = { .len = TC_RTAB_SIZE },
 128        [TCA_POLICE_PEAKRATE]   = { .len = TC_RTAB_SIZE },
 129        [TCA_POLICE_AVRATE]     = { .type = NLA_U32 },
 130        [TCA_POLICE_RESULT]     = { .type = NLA_U32 },
 131};
 132
 133static int tcf_act_police_locate(struct nlattr *nla, struct nlattr *est,
 134                                 struct tc_action *a, int ovr, int bind)
 135{
 136        unsigned int h;
 137        int ret = 0, err;
 138        struct nlattr *tb[TCA_POLICE_MAX + 1];
 139        struct tc_police *parm;
 140        struct tcf_police *police;
 141        struct qdisc_rate_table *R_tab = NULL, *P_tab = NULL;
 142        int size;
 143
 144        if (nla == NULL)
 145                return -EINVAL;
 146
 147        err = nla_parse_nested(tb, TCA_POLICE_MAX, nla, police_policy);
 148        if (err < 0)
 149                return err;
 150
 151        if (tb[TCA_POLICE_TBF] == NULL)
 152                return -EINVAL;
 153        size = nla_len(tb[TCA_POLICE_TBF]);
 154        if (size != sizeof(*parm) && size != sizeof(struct tc_police_compat))
 155                return -EINVAL;
 156        parm = nla_data(tb[TCA_POLICE_TBF]);
 157
 158        if (parm->index) {
 159                struct tcf_common *pc;
 160
 161                pc = tcf_hash_lookup(parm->index, &police_hash_info);
 162                if (pc != NULL) {
 163                        a->priv = pc;
 164                        police = to_police(pc);
 165                        if (bind) {
 166                                police->tcf_bindcnt += 1;
 167                                police->tcf_refcnt += 1;
 168                        }
 169                        if (ovr)
 170                                goto override;
 171                        return ret;
 172                }
 173        }
 174
 175        police = kzalloc(sizeof(*police), GFP_KERNEL);
 176        if (police == NULL)
 177                return -ENOMEM;
 178        ret = ACT_P_CREATED;
 179        police->tcf_refcnt = 1;
 180        spin_lock_init(&police->tcf_lock);
 181        if (bind)
 182                police->tcf_bindcnt = 1;
 183override:
 184        if (parm->rate.rate) {
 185                err = -ENOMEM;
 186                R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE]);
 187                if (R_tab == NULL)
 188                        goto failure;
 189
 190                if (parm->peakrate.rate) {
 191                        P_tab = qdisc_get_rtab(&parm->peakrate,
 192                                               tb[TCA_POLICE_PEAKRATE]);
 193                        if (P_tab == NULL)
 194                                goto failure;
 195                }
 196        }
 197
 198        spin_lock_bh(&police->tcf_lock);
 199        if (est) {
 200                err = gen_replace_estimator(&police->tcf_bstats,
 201                                            &police->tcf_rate_est,
 202                                            &police->tcf_lock, est);
 203                if (err)
 204                        goto failure_unlock;
 205        } else if (tb[TCA_POLICE_AVRATE] &&
 206                   (ret == ACT_P_CREATED ||
 207                    !gen_estimator_active(&police->tcf_bstats,
 208                                          &police->tcf_rate_est))) {
 209                err = -EINVAL;
 210                goto failure_unlock;
 211        }
 212
 213        /* No failure allowed after this point */
 214        if (R_tab != NULL) {
 215                qdisc_put_rtab(police->tcfp_R_tab);
 216                police->tcfp_R_tab = R_tab;
 217        }
 218        if (P_tab != NULL) {
 219                qdisc_put_rtab(police->tcfp_P_tab);
 220                police->tcfp_P_tab = P_tab;
 221        }
 222
 223        if (tb[TCA_POLICE_RESULT])
 224                police->tcfp_result = nla_get_u32(tb[TCA_POLICE_RESULT]);
 225        police->tcfp_toks = police->tcfp_burst = parm->burst;
 226        police->tcfp_mtu = parm->mtu;
 227        if (police->tcfp_mtu == 0) {
 228                police->tcfp_mtu = ~0;
 229                if (police->tcfp_R_tab)
 230                        police->tcfp_mtu = 255<<police->tcfp_R_tab->rate.cell_log;
 231        }
 232        if (police->tcfp_P_tab)
 233                police->tcfp_ptoks = L2T_P(police, police->tcfp_mtu);
 234        police->tcf_action = parm->action;
 235
 236        if (tb[TCA_POLICE_AVRATE])
 237                police->tcfp_ewma_rate = nla_get_u32(tb[TCA_POLICE_AVRATE]);
 238
 239        spin_unlock_bh(&police->tcf_lock);
 240        if (ret != ACT_P_CREATED)
 241                return ret;
 242
 243        police->tcfp_t_c = psched_get_time();
 244        police->tcf_index = parm->index ? parm->index :
 245                tcf_hash_new_index(&police_idx_gen, &police_hash_info);
 246        h = tcf_hash(police->tcf_index, POL_TAB_MASK);
 247        write_lock_bh(&police_lock);
 248        police->tcf_next = tcf_police_ht[h];
 249        tcf_police_ht[h] = &police->common;
 250        write_unlock_bh(&police_lock);
 251
 252        a->priv = police;
 253        return ret;
 254
 255failure_unlock:
 256        spin_unlock_bh(&police->tcf_lock);
 257failure:
 258        if (P_tab)
 259                qdisc_put_rtab(P_tab);
 260        if (R_tab)
 261                qdisc_put_rtab(R_tab);
 262        if (ret == ACT_P_CREATED)
 263                kfree(police);
 264        return err;
 265}
 266
 267static int tcf_act_police_cleanup(struct tc_action *a, int bind)
 268{
 269        struct tcf_police *p = a->priv;
 270        int ret = 0;
 271
 272        if (p != NULL) {
 273                if (bind)
 274                        p->tcf_bindcnt--;
 275
 276                p->tcf_refcnt--;
 277                if (p->tcf_refcnt <= 0 && !p->tcf_bindcnt) {
 278                        tcf_police_destroy(p);
 279                        ret = 1;
 280                }
 281        }
 282        return ret;
 283}
 284
 285static int tcf_act_police(struct sk_buff *skb, const struct tc_action *a,
 286                          struct tcf_result *res)
 287{
 288        struct tcf_police *police = a->priv;
 289        psched_time_t now;
 290        long toks;
 291        long ptoks = 0;
 292
 293        spin_lock(&police->tcf_lock);
 294
 295        bstats_update(&police->tcf_bstats, skb);
 296
 297        if (police->tcfp_ewma_rate &&
 298            police->tcf_rate_est.bps >= police->tcfp_ewma_rate) {
 299                police->tcf_qstats.overlimits++;
 300                if (police->tcf_action == TC_ACT_SHOT)
 301                        police->tcf_qstats.drops++;
 302                spin_unlock(&police->tcf_lock);
 303                return police->tcf_action;
 304        }
 305
 306        if (qdisc_pkt_len(skb) <= police->tcfp_mtu) {
 307                if (police->tcfp_R_tab == NULL) {
 308                        spin_unlock(&police->tcf_lock);
 309                        return police->tcfp_result;
 310                }
 311
 312                now = psched_get_time();
 313                toks = psched_tdiff_bounded(now, police->tcfp_t_c,
 314                                            police->tcfp_burst);
 315                if (police->tcfp_P_tab) {
 316                        ptoks = toks + police->tcfp_ptoks;
 317                        if (ptoks > (long)L2T_P(police, police->tcfp_mtu))
 318                                ptoks = (long)L2T_P(police, police->tcfp_mtu);
 319                        ptoks -= L2T_P(police, qdisc_pkt_len(skb));
 320                }
 321                toks += police->tcfp_toks;
 322                if (toks > (long)police->tcfp_burst)
 323                        toks = police->tcfp_burst;
 324                toks -= L2T(police, qdisc_pkt_len(skb));
 325                if ((toks|ptoks) >= 0) {
 326                        police->tcfp_t_c = now;
 327                        police->tcfp_toks = toks;
 328                        police->tcfp_ptoks = ptoks;
 329                        spin_unlock(&police->tcf_lock);
 330                        return police->tcfp_result;
 331                }
 332        }
 333
 334        police->tcf_qstats.overlimits++;
 335        if (police->tcf_action == TC_ACT_SHOT)
 336                police->tcf_qstats.drops++;
 337        spin_unlock(&police->tcf_lock);
 338        return police->tcf_action;
 339}
 340
 341static int
 342tcf_act_police_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
 343{
 344        unsigned char *b = skb_tail_pointer(skb);
 345        struct tcf_police *police = a->priv;
 346        struct tc_police opt = {
 347                .index = police->tcf_index,
 348                .action = police->tcf_action,
 349                .mtu = police->tcfp_mtu,
 350                .burst = police->tcfp_burst,
 351                .refcnt = police->tcf_refcnt - ref,
 352                .bindcnt = police->tcf_bindcnt - bind,
 353        };
 354
 355        if (police->tcfp_R_tab)
 356                opt.rate = police->tcfp_R_tab->rate;
 357        if (police->tcfp_P_tab)
 358                opt.peakrate = police->tcfp_P_tab->rate;
 359        if (nla_put(skb, TCA_POLICE_TBF, sizeof(opt), &opt))
 360                goto nla_put_failure;
 361        if (police->tcfp_result &&
 362            nla_put_u32(skb, TCA_POLICE_RESULT, police->tcfp_result))
 363                goto nla_put_failure;
 364        if (police->tcfp_ewma_rate &&
 365            nla_put_u32(skb, TCA_POLICE_AVRATE, police->tcfp_ewma_rate))
 366                goto nla_put_failure;
 367        return skb->len;
 368
 369nla_put_failure:
 370        nlmsg_trim(skb, b);
 371        return -1;
 372}
 373
 374MODULE_AUTHOR("Alexey Kuznetsov");
 375MODULE_DESCRIPTION("Policing actions");
 376MODULE_LICENSE("GPL");
 377
 378static struct tc_action_ops act_police_ops = {
 379        .kind           =       "police",
 380        .hinfo          =       &police_hash_info,
 381        .type           =       TCA_ID_POLICE,
 382        .capab          =       TCA_CAP_NONE,
 383        .owner          =       THIS_MODULE,
 384        .act            =       tcf_act_police,
 385        .dump           =       tcf_act_police_dump,
 386        .cleanup        =       tcf_act_police_cleanup,
 387        .lookup         =       tcf_hash_search,
 388        .init           =       tcf_act_police_locate,
 389        .walk           =       tcf_act_police_walker
 390};
 391
 392static int __init
 393police_init_module(void)
 394{
 395        return tcf_register_action(&act_police_ops);
 396}
 397
 398static void __exit
 399police_cleanup_module(void)
 400{
 401        tcf_unregister_action(&act_police_ops);
 402}
 403
 404module_init(police_init_module);
 405module_exit(police_cleanup_module);
 406
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.