linux/net/sched/sch_drr.c
<<
>>
Prefs
   1/*
   2 * net/sched/sch_drr.c         Deficit Round Robin scheduler
   3 *
   4 * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License
   8 * version 2 as published by the Free Software Foundation.
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/slab.h>
  13#include <linux/init.h>
  14#include <linux/errno.h>
  15#include <linux/netdevice.h>
  16#include <linux/pkt_sched.h>
  17#include <net/sch_generic.h>
  18#include <net/pkt_sched.h>
  19#include <net/pkt_cls.h>
  20
  21struct drr_class {
  22        struct Qdisc_class_common       common;
  23        unsigned int                    refcnt;
  24        unsigned int                    filter_cnt;
  25
  26        struct gnet_stats_basic_packed          bstats;
  27        struct gnet_stats_queue         qstats;
  28        struct gnet_stats_rate_est64    rate_est;
  29        struct list_head                alist;
  30        struct Qdisc                    *qdisc;
  31
  32        u32                             quantum;
  33        u32                             deficit;
  34};
  35
  36struct drr_sched {
  37        struct list_head                active;
  38        struct tcf_proto __rcu          *filter_list;
  39        struct Qdisc_class_hash         clhash;
  40};
  41
  42static struct drr_class *drr_find_class(struct Qdisc *sch, u32 classid)
  43{
  44        struct drr_sched *q = qdisc_priv(sch);
  45        struct Qdisc_class_common *clc;
  46
  47        clc = qdisc_class_find(&q->clhash, classid);
  48        if (clc == NULL)
  49                return NULL;
  50        return container_of(clc, struct drr_class, common);
  51}
  52
  53static void drr_purge_queue(struct drr_class *cl)
  54{
  55        unsigned int len = cl->qdisc->q.qlen;
  56
  57        qdisc_reset(cl->qdisc);
  58        qdisc_tree_decrease_qlen(cl->qdisc, len);
  59}
  60
  61static const struct nla_policy drr_policy[TCA_DRR_MAX + 1] = {
  62        [TCA_DRR_QUANTUM]       = { .type = NLA_U32 },
  63};
  64
  65static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
  66                            struct nlattr **tca, unsigned long *arg)
  67{
  68        struct drr_sched *q = qdisc_priv(sch);
  69        struct drr_class *cl = (struct drr_class *)*arg;
  70        struct nlattr *opt = tca[TCA_OPTIONS];
  71        struct nlattr *tb[TCA_DRR_MAX + 1];
  72        u32 quantum;
  73        int err;
  74
  75        if (!opt)
  76                return -EINVAL;
  77
  78        err = nla_parse_nested(tb, TCA_DRR_MAX, opt, drr_policy);
  79        if (err < 0)
  80                return err;
  81
  82        if (tb[TCA_DRR_QUANTUM]) {
  83                quantum = nla_get_u32(tb[TCA_DRR_QUANTUM]);
  84                if (quantum == 0)
  85                        return -EINVAL;
  86        } else
  87                quantum = psched_mtu(qdisc_dev(sch));
  88
  89        if (cl != NULL) {
  90                if (tca[TCA_RATE]) {
  91                        err = gen_replace_estimator(&cl->bstats, NULL,
  92                                                    &cl->rate_est,
  93                                                    qdisc_root_sleeping_lock(sch),
  94                                                    tca[TCA_RATE]);
  95                        if (err)
  96                                return err;
  97                }
  98
  99                sch_tree_lock(sch);
 100                if (tb[TCA_DRR_QUANTUM])
 101                        cl->quantum = quantum;
 102                sch_tree_unlock(sch);
 103
 104                return 0;
 105        }
 106
 107        cl = kzalloc(sizeof(struct drr_class), GFP_KERNEL);
 108        if (cl == NULL)
 109                return -ENOBUFS;
 110
 111        cl->refcnt         = 1;
 112        cl->common.classid = classid;
 113        cl->quantum        = quantum;
 114        cl->qdisc          = qdisc_create_dflt(sch->dev_queue,
 115                                               &pfifo_qdisc_ops, classid);
 116        if (cl->qdisc == NULL)
 117                cl->qdisc = &noop_qdisc;
 118
 119        if (tca[TCA_RATE]) {
 120                err = gen_replace_estimator(&cl->bstats, NULL, &cl->rate_est,
 121                                            qdisc_root_sleeping_lock(sch),
 122                                            tca[TCA_RATE]);
 123                if (err) {
 124                        qdisc_destroy(cl->qdisc);
 125                        kfree(cl);
 126                        return err;
 127                }
 128        }
 129
 130        sch_tree_lock(sch);
 131        qdisc_class_hash_insert(&q->clhash, &cl->common);
 132        sch_tree_unlock(sch);
 133
 134        qdisc_class_hash_grow(sch, &q->clhash);
 135
 136        *arg = (unsigned long)cl;
 137        return 0;
 138}
 139
 140static void drr_destroy_class(struct Qdisc *sch, struct drr_class *cl)
 141{
 142        gen_kill_estimator(&cl->bstats, &cl->rate_est);
 143        qdisc_destroy(cl->qdisc);
 144        kfree(cl);
 145}
 146
 147static int drr_delete_class(struct Qdisc *sch, unsigned long arg)
 148{
 149        struct drr_sched *q = qdisc_priv(sch);
 150        struct drr_class *cl = (struct drr_class *)arg;
 151
 152        if (cl->filter_cnt > 0)
 153                return -EBUSY;
 154
 155        sch_tree_lock(sch);
 156
 157        drr_purge_queue(cl);
 158        qdisc_class_hash_remove(&q->clhash, &cl->common);
 159
 160        BUG_ON(--cl->refcnt == 0);
 161        /*
 162         * This shouldn't happen: we "hold" one cops->get() when called
 163         * from tc_ctl_tclass; the destroy method is done from cops->put().
 164         */
 165
 166        sch_tree_unlock(sch);
 167        return 0;
 168}
 169
 170static unsigned long drr_get_class(struct Qdisc *sch, u32 classid)
 171{
 172        struct drr_class *cl = drr_find_class(sch, classid);
 173
 174        if (cl != NULL)
 175                cl->refcnt++;
 176
 177        return (unsigned long)cl;
 178}
 179
 180static void drr_put_class(struct Qdisc *sch, unsigned long arg)
 181{
 182        struct drr_class *cl = (struct drr_class *)arg;
 183
 184        if (--cl->refcnt == 0)
 185                drr_destroy_class(sch, cl);
 186}
 187
 188static struct tcf_proto __rcu **drr_tcf_chain(struct Qdisc *sch,
 189                                              unsigned long cl)
 190{
 191        struct drr_sched *q = qdisc_priv(sch);
 192
 193        if (cl)
 194                return NULL;
 195
 196        return &q->filter_list;
 197}
 198
 199static unsigned long drr_bind_tcf(struct Qdisc *sch, unsigned long parent,
 200                                  u32 classid)
 201{
 202        struct drr_class *cl = drr_find_class(sch, classid);
 203
 204        if (cl != NULL)
 205                cl->filter_cnt++;
 206
 207        return (unsigned long)cl;
 208}
 209
 210static void drr_unbind_tcf(struct Qdisc *sch, unsigned long arg)
 211{
 212        struct drr_class *cl = (struct drr_class *)arg;
 213
 214        cl->filter_cnt--;
 215}
 216
 217static int drr_graft_class(struct Qdisc *sch, unsigned long arg,
 218                           struct Qdisc *new, struct Qdisc **old)
 219{
 220        struct drr_class *cl = (struct drr_class *)arg;
 221
 222        if (new == NULL) {
 223                new = qdisc_create_dflt(sch->dev_queue,
 224                                        &pfifo_qdisc_ops, cl->common.classid);
 225                if (new == NULL)
 226                        new = &noop_qdisc;
 227        }
 228
 229        sch_tree_lock(sch);
 230        drr_purge_queue(cl);
 231        *old = cl->qdisc;
 232        cl->qdisc = new;
 233        sch_tree_unlock(sch);
 234        return 0;
 235}
 236
 237static struct Qdisc *drr_class_leaf(struct Qdisc *sch, unsigned long arg)
 238{
 239        struct drr_class *cl = (struct drr_class *)arg;
 240
 241        return cl->qdisc;
 242}
 243
 244static void drr_qlen_notify(struct Qdisc *csh, unsigned long arg)
 245{
 246        struct drr_class *cl = (struct drr_class *)arg;
 247
 248        if (cl->qdisc->q.qlen == 0)
 249                list_del(&cl->alist);
 250}
 251
 252static int drr_dump_class(struct Qdisc *sch, unsigned long arg,
 253                          struct sk_buff *skb, struct tcmsg *tcm)
 254{
 255        struct drr_class *cl = (struct drr_class *)arg;
 256        struct nlattr *nest;
 257
 258        tcm->tcm_parent = TC_H_ROOT;
 259        tcm->tcm_handle = cl->common.classid;
 260        tcm->tcm_info   = cl->qdisc->handle;
 261
 262        nest = nla_nest_start(skb, TCA_OPTIONS);
 263        if (nest == NULL)
 264                goto nla_put_failure;
 265        if (nla_put_u32(skb, TCA_DRR_QUANTUM, cl->quantum))
 266                goto nla_put_failure;
 267        return nla_nest_end(skb, nest);
 268
 269nla_put_failure:
 270        nla_nest_cancel(skb, nest);
 271        return -EMSGSIZE;
 272}
 273
 274static int drr_dump_class_stats(struct Qdisc *sch, unsigned long arg,
 275                                struct gnet_dump *d)
 276{
 277        struct drr_class *cl = (struct drr_class *)arg;
 278        __u32 qlen = cl->qdisc->q.qlen;
 279        struct tc_drr_stats xstats;
 280
 281        memset(&xstats, 0, sizeof(xstats));
 282        if (qlen)
 283                xstats.deficit = cl->deficit;
 284
 285        if (gnet_stats_copy_basic(d, NULL, &cl->bstats) < 0 ||
 286            gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 ||
 287            gnet_stats_copy_queue(d, NULL, &cl->qdisc->qstats, qlen) < 0)
 288                return -1;
 289
 290        return gnet_stats_copy_app(d, &xstats, sizeof(xstats));
 291}
 292
 293static void drr_walk(struct Qdisc *sch, struct qdisc_walker *arg)
 294{
 295        struct drr_sched *q = qdisc_priv(sch);
 296        struct drr_class *cl;
 297        unsigned int i;
 298
 299        if (arg->stop)
 300                return;
 301
 302        for (i = 0; i < q->clhash.hashsize; i++) {
 303                hlist_for_each_entry(cl, &q->clhash.hash[i], common.hnode) {
 304                        if (arg->count < arg->skip) {
 305                                arg->count++;
 306                                continue;
 307                        }
 308                        if (arg->fn(sch, (unsigned long)cl, arg) < 0) {
 309                                arg->stop = 1;
 310                                return;
 311                        }
 312                        arg->count++;
 313                }
 314        }
 315}
 316
 317static struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch,
 318                                      int *qerr)
 319{
 320        struct drr_sched *q = qdisc_priv(sch);
 321        struct drr_class *cl;
 322        struct tcf_result res;
 323        struct tcf_proto *fl;
 324        int result;
 325
 326        if (TC_H_MAJ(skb->priority ^ sch->handle) == 0) {
 327                cl = drr_find_class(sch, skb->priority);
 328                if (cl != NULL)
 329                        return cl;
 330        }
 331
 332        *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
 333        fl = rcu_dereference_bh(q->filter_list);
 334        result = tc_classify(skb, fl, &res, false);
 335        if (result >= 0) {
 336#ifdef CONFIG_NET_CLS_ACT
 337                switch (result) {
 338                case TC_ACT_QUEUED:
 339                case TC_ACT_STOLEN:
 340                        *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
 341                case TC_ACT_SHOT:
 342                        return NULL;
 343                }
 344#endif
 345                cl = (struct drr_class *)res.class;
 346                if (cl == NULL)
 347                        cl = drr_find_class(sch, res.classid);
 348                return cl;
 349        }
 350        return NULL;
 351}
 352
 353static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 354{
 355        struct drr_sched *q = qdisc_priv(sch);
 356        struct drr_class *cl;
 357        int err = 0;
 358
 359        cl = drr_classify(skb, sch, &err);
 360        if (cl == NULL) {
 361                if (err & __NET_XMIT_BYPASS)
 362                        qdisc_qstats_drop(sch);
 363                kfree_skb(skb);
 364                return err;
 365        }
 366
 367        err = qdisc_enqueue(skb, cl->qdisc);
 368        if (unlikely(err != NET_XMIT_SUCCESS)) {
 369                if (net_xmit_drop_count(err)) {
 370                        cl->qstats.drops++;
 371                        qdisc_qstats_drop(sch);
 372                }
 373                return err;
 374        }
 375
 376        if (cl->qdisc->q.qlen == 1) {
 377                list_add_tail(&cl->alist, &q->active);
 378                cl->deficit = cl->quantum;
 379        }
 380
 381        sch->q.qlen++;
 382        return err;
 383}
 384
 385static struct sk_buff *drr_dequeue(struct Qdisc *sch)
 386{
 387        struct drr_sched *q = qdisc_priv(sch);
 388        struct drr_class *cl;
 389        struct sk_buff *skb;
 390        unsigned int len;
 391
 392        if (list_empty(&q->active))
 393                goto out;
 394        while (1) {
 395                cl = list_first_entry(&q->active, struct drr_class, alist);
 396                skb = cl->qdisc->ops->peek(cl->qdisc);
 397                if (skb == NULL) {
 398                        qdisc_warn_nonwc(__func__, cl->qdisc);
 399                        goto out;
 400                }
 401
 402                len = qdisc_pkt_len(skb);
 403                if (len <= cl->deficit) {
 404                        cl->deficit -= len;
 405                        skb = qdisc_dequeue_peeked(cl->qdisc);
 406                        if (cl->qdisc->q.qlen == 0)
 407                                list_del(&cl->alist);
 408
 409                        bstats_update(&cl->bstats, skb);
 410                        qdisc_bstats_update(sch, skb);
 411                        sch->q.qlen--;
 412                        return skb;
 413                }
 414
 415                cl->deficit += cl->quantum;
 416                list_move_tail(&cl->alist, &q->active);
 417        }
 418out:
 419        return NULL;
 420}
 421
 422static unsigned int drr_drop(struct Qdisc *sch)
 423{
 424        struct drr_sched *q = qdisc_priv(sch);
 425        struct drr_class *cl;
 426        unsigned int len;
 427
 428        list_for_each_entry(cl, &q->active, alist) {
 429                if (cl->qdisc->ops->drop) {
 430                        len = cl->qdisc->ops->drop(cl->qdisc);
 431                        if (len > 0) {
 432                                sch->q.qlen--;
 433                                if (cl->qdisc->q.qlen == 0)
 434                                        list_del(&cl->alist);
 435                                return len;
 436                        }
 437                }
 438        }
 439        return 0;
 440}
 441
 442static int drr_init_qdisc(struct Qdisc *sch, struct nlattr *opt)
 443{
 444        struct drr_sched *q = qdisc_priv(sch);
 445        int err;
 446
 447        err = qdisc_class_hash_init(&q->clhash);
 448        if (err < 0)
 449                return err;
 450        INIT_LIST_HEAD(&q->active);
 451        return 0;
 452}
 453
 454static void drr_reset_qdisc(struct Qdisc *sch)
 455{
 456        struct drr_sched *q = qdisc_priv(sch);
 457        struct drr_class *cl;
 458        unsigned int i;
 459
 460        for (i = 0; i < q->clhash.hashsize; i++) {
 461                hlist_for_each_entry(cl, &q->clhash.hash[i], common.hnode) {
 462                        if (cl->qdisc->q.qlen)
 463                                list_del(&cl->alist);
 464                        qdisc_reset(cl->qdisc);
 465                }
 466        }
 467        sch->q.qlen = 0;
 468}
 469
 470static void drr_destroy_qdisc(struct Qdisc *sch)
 471{
 472        struct drr_sched *q = qdisc_priv(sch);
 473        struct drr_class *cl;
 474        struct hlist_node *next;
 475        unsigned int i;
 476
 477        tcf_destroy_chain(&q->filter_list);
 478
 479        for (i = 0; i < q->clhash.hashsize; i++) {
 480                hlist_for_each_entry_safe(cl, next, &q->clhash.hash[i],
 481                                          common.hnode)
 482                        drr_destroy_class(sch, cl);
 483        }
 484        qdisc_class_hash_destroy(&q->clhash);
 485}
 486
 487static const struct Qdisc_class_ops drr_class_ops = {
 488        .change         = drr_change_class,
 489        .delete         = drr_delete_class,
 490        .get            = drr_get_class,
 491        .put            = drr_put_class,
 492        .tcf_chain      = drr_tcf_chain,
 493        .bind_tcf       = drr_bind_tcf,
 494        .unbind_tcf     = drr_unbind_tcf,
 495        .graft          = drr_graft_class,
 496        .leaf           = drr_class_leaf,
 497        .qlen_notify    = drr_qlen_notify,
 498        .dump           = drr_dump_class,
 499        .dump_stats     = drr_dump_class_stats,
 500        .walk           = drr_walk,
 501};
 502
 503static struct Qdisc_ops drr_qdisc_ops __read_mostly = {
 504        .cl_ops         = &drr_class_ops,
 505        .id             = "drr",
 506        .priv_size      = sizeof(struct drr_sched),
 507        .enqueue        = drr_enqueue,
 508        .dequeue        = drr_dequeue,
 509        .peek           = qdisc_peek_dequeued,
 510        .drop           = drr_drop,
 511        .init           = drr_init_qdisc,
 512        .reset          = drr_reset_qdisc,
 513        .destroy        = drr_destroy_qdisc,
 514        .owner          = THIS_MODULE,
 515};
 516
 517static int __init drr_init(void)
 518{
 519        return register_qdisc(&drr_qdisc_ops);
 520}
 521
 522static void __exit drr_exit(void)
 523{
 524        unregister_qdisc(&drr_qdisc_ops);
 525}
 526
 527module_init(drr_init);
 528module_exit(drr_exit);
 529MODULE_LICENSE("GPL");
 530