linux/net/sched/sch_prio.c
<<
>>
Prefs
   1/*
   2 * net/sched/sch_prio.c Simple 3-band priority "scheduler".
   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 * Fixes:       19990609: J Hadi Salim <hadi@nortelnetworks.com>:
  11 *              Init --  EINVAL when opt undefined
  12 */
  13
  14#include <linux/module.h>
  15#include <linux/types.h>
  16#include <linux/kernel.h>
  17#include <linux/string.h>
  18#include <linux/errno.h>
  19#include <linux/skbuff.h>
  20#include <net/netlink.h>
  21#include <net/pkt_sched.h>
  22
  23
  24struct prio_sched_data
  25{
  26        int bands;
  27        struct tcf_proto *filter_list;
  28        u8  prio2band[TC_PRIO_MAX+1];
  29        struct Qdisc *queues[TCQ_PRIO_BANDS];
  30};
  31
  32
  33static struct Qdisc *
  34prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
  35{
  36        struct prio_sched_data *q = qdisc_priv(sch);
  37        u32 band = skb->priority;
  38        struct tcf_result res;
  39        int err;
  40
  41        *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
  42        if (TC_H_MAJ(skb->priority) != sch->handle) {
  43                err = tc_classify(skb, q->filter_list, &res);
  44#ifdef CONFIG_NET_CLS_ACT
  45                switch (err) {
  46                case TC_ACT_STOLEN:
  47                case TC_ACT_QUEUED:
  48                        *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
  49                case TC_ACT_SHOT:
  50                        return NULL;
  51                }
  52#endif
  53                if (!q->filter_list || err < 0) {
  54                        if (TC_H_MAJ(band))
  55                                band = 0;
  56                        return q->queues[q->prio2band[band&TC_PRIO_MAX]];
  57                }
  58                band = res.classid;
  59        }
  60        band = TC_H_MIN(band) - 1;
  61        if (band >= q->bands)
  62                return q->queues[q->prio2band[0]];
  63
  64        return q->queues[band];
  65}
  66
  67static int
  68prio_enqueue(struct sk_buff *skb, struct Qdisc *sch)
  69{
  70        struct Qdisc *qdisc;
  71        int ret;
  72
  73        qdisc = prio_classify(skb, sch, &ret);
  74#ifdef CONFIG_NET_CLS_ACT
  75        if (qdisc == NULL) {
  76
  77                if (ret & __NET_XMIT_BYPASS)
  78                        sch->qstats.drops++;
  79                kfree_skb(skb);
  80                return ret;
  81        }
  82#endif
  83
  84        ret = qdisc_enqueue(skb, qdisc);
  85        if (ret == NET_XMIT_SUCCESS) {
  86                sch->bstats.bytes += qdisc_pkt_len(skb);
  87                sch->bstats.packets++;
  88                sch->q.qlen++;
  89                return NET_XMIT_SUCCESS;
  90        }
  91        if (net_xmit_drop_count(ret))
  92                sch->qstats.drops++;
  93        return ret;
  94}
  95
  96
  97static int
  98prio_requeue(struct sk_buff *skb, struct Qdisc* sch)
  99{
 100        struct Qdisc *qdisc;
 101        int ret;
 102
 103        qdisc = prio_classify(skb, sch, &ret);
 104#ifdef CONFIG_NET_CLS_ACT
 105        if (qdisc == NULL) {
 106                if (ret & __NET_XMIT_BYPASS)
 107                        sch->qstats.drops++;
 108                kfree_skb(skb);
 109                return ret;
 110        }
 111#endif
 112
 113        if ((ret = qdisc->ops->requeue(skb, qdisc)) == NET_XMIT_SUCCESS) {
 114                sch->q.qlen++;
 115                sch->qstats.requeues++;
 116                return NET_XMIT_SUCCESS;
 117        }
 118        if (net_xmit_drop_count(ret))
 119                sch->qstats.drops++;
 120        return ret;
 121}
 122
 123
 124static struct sk_buff *prio_dequeue(struct Qdisc* sch)
 125{
 126        struct prio_sched_data *q = qdisc_priv(sch);
 127        int prio;
 128
 129        for (prio = 0; prio < q->bands; prio++) {
 130                struct Qdisc *qdisc = q->queues[prio];
 131                struct sk_buff *skb = qdisc->dequeue(qdisc);
 132                if (skb) {
 133                        sch->q.qlen--;
 134                        return skb;
 135                }
 136        }
 137        return NULL;
 138
 139}
 140
 141static unsigned int prio_drop(struct Qdisc* sch)
 142{
 143        struct prio_sched_data *q = qdisc_priv(sch);
 144        int prio;
 145        unsigned int len;
 146        struct Qdisc *qdisc;
 147
 148        for (prio = q->bands-1; prio >= 0; prio--) {
 149                qdisc = q->queues[prio];
 150                if (qdisc->ops->drop && (len = qdisc->ops->drop(qdisc)) != 0) {
 151                        sch->q.qlen--;
 152                        return len;
 153                }
 154        }
 155        return 0;
 156}
 157
 158
 159static void
 160prio_reset(struct Qdisc* sch)
 161{
 162        int prio;
 163        struct prio_sched_data *q = qdisc_priv(sch);
 164
 165        for (prio=0; prio<q->bands; prio++)
 166                qdisc_reset(q->queues[prio]);
 167        sch->q.qlen = 0;
 168}
 169
 170static void
 171prio_destroy(struct Qdisc* sch)
 172{
 173        int prio;
 174        struct prio_sched_data *q = qdisc_priv(sch);
 175
 176        tcf_destroy_chain(&q->filter_list);
 177        for (prio=0; prio<q->bands; prio++)
 178                qdisc_destroy(q->queues[prio]);
 179}
 180
 181static int prio_tune(struct Qdisc *sch, struct nlattr *opt)
 182{
 183        struct prio_sched_data *q = qdisc_priv(sch);
 184        struct tc_prio_qopt *qopt;
 185        int i;
 186
 187        if (nla_len(opt) < sizeof(*qopt))
 188                return -EINVAL;
 189        qopt = nla_data(opt);
 190
 191        if (qopt->bands > TCQ_PRIO_BANDS || qopt->bands < 2)
 192                return -EINVAL;
 193
 194        for (i=0; i<=TC_PRIO_MAX; i++) {
 195                if (qopt->priomap[i] >= qopt->bands)
 196                        return -EINVAL;
 197        }
 198
 199        sch_tree_lock(sch);
 200        q->bands = qopt->bands;
 201        memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1);
 202
 203        for (i=q->bands; i<TCQ_PRIO_BANDS; i++) {
 204                struct Qdisc *child = xchg(&q->queues[i], &noop_qdisc);
 205                if (child != &noop_qdisc) {
 206                        qdisc_tree_decrease_qlen(child, child->q.qlen);
 207                        qdisc_destroy(child);
 208                }
 209        }
 210        sch_tree_unlock(sch);
 211
 212        for (i=0; i<q->bands; i++) {
 213                if (q->queues[i] == &noop_qdisc) {
 214                        struct Qdisc *child;
 215                        child = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue,
 216                                                  &pfifo_qdisc_ops,
 217                                                  TC_H_MAKE(sch->handle, i + 1));
 218                        if (child) {
 219                                sch_tree_lock(sch);
 220                                child = xchg(&q->queues[i], child);
 221
 222                                if (child != &noop_qdisc) {
 223                                        qdisc_tree_decrease_qlen(child,
 224                                                                 child->q.qlen);
 225                                        qdisc_destroy(child);
 226                                }
 227                                sch_tree_unlock(sch);
 228                        }
 229                }
 230        }
 231        return 0;
 232}
 233
 234static int prio_init(struct Qdisc *sch, struct nlattr *opt)
 235{
 236        struct prio_sched_data *q = qdisc_priv(sch);
 237        int i;
 238
 239        for (i=0; i<TCQ_PRIO_BANDS; i++)
 240                q->queues[i] = &noop_qdisc;
 241
 242        if (opt == NULL) {
 243                return -EINVAL;
 244        } else {
 245                int err;
 246
 247                if ((err= prio_tune(sch, opt)) != 0)
 248                        return err;
 249        }
 250        return 0;
 251}
 252
 253static int prio_dump(struct Qdisc *sch, struct sk_buff *skb)
 254{
 255        struct prio_sched_data *q = qdisc_priv(sch);
 256        unsigned char *b = skb_tail_pointer(skb);
 257        struct tc_prio_qopt opt;
 258
 259        opt.bands = q->bands;
 260        memcpy(&opt.priomap, q->prio2band, TC_PRIO_MAX+1);
 261
 262        NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
 263
 264        return skb->len;
 265
 266nla_put_failure:
 267        nlmsg_trim(skb, b);
 268        return -1;
 269}
 270
 271static int prio_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
 272                      struct Qdisc **old)
 273{
 274        struct prio_sched_data *q = qdisc_priv(sch);
 275        unsigned long band = arg - 1;
 276
 277        if (band >= q->bands)
 278                return -EINVAL;
 279
 280        if (new == NULL)
 281                new = &noop_qdisc;
 282
 283        sch_tree_lock(sch);
 284        *old = q->queues[band];
 285        q->queues[band] = new;
 286        qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
 287        qdisc_reset(*old);
 288        sch_tree_unlock(sch);
 289
 290        return 0;
 291}
 292
 293static struct Qdisc *
 294prio_leaf(struct Qdisc *sch, unsigned long arg)
 295{
 296        struct prio_sched_data *q = qdisc_priv(sch);
 297        unsigned long band = arg - 1;
 298
 299        if (band >= q->bands)
 300                return NULL;
 301
 302        return q->queues[band];
 303}
 304
 305static unsigned long prio_get(struct Qdisc *sch, u32 classid)
 306{
 307        struct prio_sched_data *q = qdisc_priv(sch);
 308        unsigned long band = TC_H_MIN(classid);
 309
 310        if (band - 1 >= q->bands)
 311                return 0;
 312        return band;
 313}
 314
 315static unsigned long prio_bind(struct Qdisc *sch, unsigned long parent, u32 classid)
 316{
 317        return prio_get(sch, classid);
 318}
 319
 320
 321static void prio_put(struct Qdisc *q, unsigned long cl)
 322{
 323        return;
 324}
 325
 326static int prio_change(struct Qdisc *sch, u32 handle, u32 parent, struct nlattr **tca, unsigned long *arg)
 327{
 328        unsigned long cl = *arg;
 329        struct prio_sched_data *q = qdisc_priv(sch);
 330
 331        if (cl - 1 > q->bands)
 332                return -ENOENT;
 333        return 0;
 334}
 335
 336static int prio_delete(struct Qdisc *sch, unsigned long cl)
 337{
 338        struct prio_sched_data *q = qdisc_priv(sch);
 339        if (cl - 1 > q->bands)
 340                return -ENOENT;
 341        return 0;
 342}
 343
 344
 345static int prio_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb,
 346                           struct tcmsg *tcm)
 347{
 348        struct prio_sched_data *q = qdisc_priv(sch);
 349
 350        if (cl - 1 > q->bands)
 351                return -ENOENT;
 352        tcm->tcm_handle |= TC_H_MIN(cl);
 353        if (q->queues[cl-1])
 354                tcm->tcm_info = q->queues[cl-1]->handle;
 355        return 0;
 356}
 357
 358static int prio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
 359                                 struct gnet_dump *d)
 360{
 361        struct prio_sched_data *q = qdisc_priv(sch);
 362        struct Qdisc *cl_q;
 363
 364        cl_q = q->queues[cl - 1];
 365        if (gnet_stats_copy_basic(d, &cl_q->bstats) < 0 ||
 366            gnet_stats_copy_queue(d, &cl_q->qstats) < 0)
 367                return -1;
 368
 369        return 0;
 370}
 371
 372static void prio_walk(struct Qdisc *sch, struct qdisc_walker *arg)
 373{
 374        struct prio_sched_data *q = qdisc_priv(sch);
 375        int prio;
 376
 377        if (arg->stop)
 378                return;
 379
 380        for (prio = 0; prio < q->bands; prio++) {
 381                if (arg->count < arg->skip) {
 382                        arg->count++;
 383                        continue;
 384                }
 385                if (arg->fn(sch, prio+1, arg) < 0) {
 386                        arg->stop = 1;
 387                        break;
 388                }
 389                arg->count++;
 390        }
 391}
 392
 393static struct tcf_proto ** prio_find_tcf(struct Qdisc *sch, unsigned long cl)
 394{
 395        struct prio_sched_data *q = qdisc_priv(sch);
 396
 397        if (cl)
 398                return NULL;
 399        return &q->filter_list;
 400}
 401
 402static const struct Qdisc_class_ops prio_class_ops = {
 403        .graft          =       prio_graft,
 404        .leaf           =       prio_leaf,
 405        .get            =       prio_get,
 406        .put            =       prio_put,
 407        .change         =       prio_change,
 408        .delete         =       prio_delete,
 409        .walk           =       prio_walk,
 410        .tcf_chain      =       prio_find_tcf,
 411        .bind_tcf       =       prio_bind,
 412        .unbind_tcf     =       prio_put,
 413        .dump           =       prio_dump_class,
 414        .dump_stats     =       prio_dump_class_stats,
 415};
 416
 417static struct Qdisc_ops prio_qdisc_ops __read_mostly = {
 418        .next           =       NULL,
 419        .cl_ops         =       &prio_class_ops,
 420        .id             =       "prio",
 421        .priv_size      =       sizeof(struct prio_sched_data),
 422        .enqueue        =       prio_enqueue,
 423        .dequeue        =       prio_dequeue,
 424        .requeue        =       prio_requeue,
 425        .drop           =       prio_drop,
 426        .init           =       prio_init,
 427        .reset          =       prio_reset,
 428        .destroy        =       prio_destroy,
 429        .change         =       prio_tune,
 430        .dump           =       prio_dump,
 431        .owner          =       THIS_MODULE,
 432};
 433
 434static int __init prio_module_init(void)
 435{
 436        return register_qdisc(&prio_qdisc_ops);
 437}
 438
 439static void __exit prio_module_exit(void)
 440{
 441        unregister_qdisc(&prio_qdisc_ops);
 442}
 443
 444module_init(prio_module_init)
 445module_exit(prio_module_exit)
 446
 447MODULE_LICENSE("GPL");
 448
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.