linux/net/sched/sch_atm.c
<<
>>
Prefs
   1/* net/sched/sch_atm.c - ATM VC selection "queueing discipline" */
   2
   3/* Written 1998-2000 by Werner Almesberger, EPFL ICA */
   4
   5#include <linux/module.h>
   6#include <linux/slab.h>
   7#include <linux/init.h>
   8#include <linux/string.h>
   9#include <linux/errno.h>
  10#include <linux/skbuff.h>
  11#include <linux/atmdev.h>
  12#include <linux/atmclip.h>
  13#include <linux/rtnetlink.h>
  14#include <linux/file.h>         /* for fput */
  15#include <net/netlink.h>
  16#include <net/pkt_sched.h>
  17
  18extern struct socket *sockfd_lookup(int fd, int *err);  /* @@@ fix this */
  19
  20/*
  21 * The ATM queuing discipline provides a framework for invoking classifiers
  22 * (aka "filters"), which in turn select classes of this queuing discipline.
  23 * Each class maps the flow(s) it is handling to a given VC. Multiple classes
  24 * may share the same VC.
  25 *
  26 * When creating a class, VCs are specified by passing the number of the open
  27 * socket descriptor by which the calling process references the VC. The kernel
  28 * keeps the VC open at least until all classes using it are removed.
  29 *
  30 * In this file, most functions are named atm_tc_* to avoid confusion with all
  31 * the atm_* in net/atm. This naming convention differs from what's used in the
  32 * rest of net/sched.
  33 *
  34 * Known bugs:
  35 *  - sometimes messes up the IP stack
  36 *  - any manipulations besides the few operations described in the README, are
  37 *    untested and likely to crash the system
  38 *  - should lock the flow while there is data in the queue (?)
  39 */
  40
  41#define VCC2FLOW(vcc) ((struct atm_flow_data *) ((vcc)->user_back))
  42
  43struct atm_flow_data {
  44        struct Qdisc            *q;     /* FIFO, TBF, etc. */
  45        struct tcf_proto        *filter_list;
  46        struct atm_vcc          *vcc;   /* VCC; NULL if VCC is closed */
  47        void                    (*old_pop)(struct atm_vcc *vcc,
  48                                           struct sk_buff *skb); /* chaining */
  49        struct atm_qdisc_data   *parent;        /* parent qdisc */
  50        struct socket           *sock;          /* for closing */
  51        u32                     classid;        /* x:y type ID */
  52        int                     ref;            /* reference count */
  53        struct gnet_stats_basic_packed  bstats;
  54        struct gnet_stats_queue qstats;
  55        struct atm_flow_data    *next;
  56        struct atm_flow_data    *excess;        /* flow for excess traffic;
  57                                                   NULL to set CLP instead */
  58        int                     hdr_len;
  59        unsigned char           hdr[0];         /* header data; MUST BE LAST */
  60};
  61
  62struct atm_qdisc_data {
  63        struct atm_flow_data    link;           /* unclassified skbs go here */
  64        struct atm_flow_data    *flows;         /* NB: "link" is also on this
  65                                                   list */
  66        struct tasklet_struct   task;           /* dequeue tasklet */
  67};
  68
  69/* ------------------------- Class/flow operations ------------------------- */
  70
  71static int find_flow(struct atm_qdisc_data *qdisc, struct atm_flow_data *flow)
  72{
  73        struct atm_flow_data *walk;
  74
  75        pr_debug("find_flow(qdisc %p,flow %p)\n", qdisc, flow);
  76        for (walk = qdisc->flows; walk; walk = walk->next)
  77                if (walk == flow)
  78                        return 1;
  79        pr_debug("find_flow: not found\n");
  80        return 0;
  81}
  82
  83static inline struct atm_flow_data *lookup_flow(struct Qdisc *sch, u32 classid)
  84{
  85        struct atm_qdisc_data *p = qdisc_priv(sch);
  86        struct atm_flow_data *flow;
  87
  88        for (flow = p->flows; flow; flow = flow->next)
  89                if (flow->classid == classid)
  90                        break;
  91        return flow;
  92}
  93
  94static int atm_tc_graft(struct Qdisc *sch, unsigned long arg,
  95                        struct Qdisc *new, struct Qdisc **old)
  96{
  97        struct atm_qdisc_data *p = qdisc_priv(sch);
  98        struct atm_flow_data *flow = (struct atm_flow_data *)arg;
  99
 100        pr_debug("atm_tc_graft(sch %p,[qdisc %p],flow %p,new %p,old %p)\n",
 101                sch, p, flow, new, old);
 102        if (!find_flow(p, flow))
 103                return -EINVAL;
 104        if (!new)
 105                new = &noop_qdisc;
 106        *old = flow->q;
 107        flow->q = new;
 108        if (*old)
 109                qdisc_reset(*old);
 110        return 0;
 111}
 112
 113static struct Qdisc *atm_tc_leaf(struct Qdisc *sch, unsigned long cl)
 114{
 115        struct atm_flow_data *flow = (struct atm_flow_data *)cl;
 116
 117        pr_debug("atm_tc_leaf(sch %p,flow %p)\n", sch, flow);
 118        return flow ? flow->q : NULL;
 119}
 120
 121static unsigned long atm_tc_get(struct Qdisc *sch, u32 classid)
 122{
 123        struct atm_qdisc_data *p __maybe_unused = qdisc_priv(sch);
 124        struct atm_flow_data *flow;
 125
 126        pr_debug("atm_tc_get(sch %p,[qdisc %p],classid %x)\n", sch, p, classid);
 127        flow = lookup_flow(sch, classid);
 128        if (flow)
 129                flow->ref++;
 130        pr_debug("atm_tc_get: flow %p\n", flow);
 131        return (unsigned long)flow;
 132}
 133
 134static unsigned long atm_tc_bind_filter(struct Qdisc *sch,
 135                                        unsigned long parent, u32 classid)
 136{
 137        return atm_tc_get(sch, classid);
 138}
 139
 140/*
 141 * atm_tc_put handles all destructions, including the ones that are explicitly
 142 * requested (atm_tc_destroy, etc.). The assumption here is that we never drop
 143 * anything that still seems to be in use.
 144 */
 145static void atm_tc_put(struct Qdisc *sch, unsigned long cl)
 146{
 147        struct atm_qdisc_data *p = qdisc_priv(sch);
 148        struct atm_flow_data *flow = (struct atm_flow_data *)cl;
 149        struct atm_flow_data **prev;
 150
 151        pr_debug("atm_tc_put(sch %p,[qdisc %p],flow %p)\n", sch, p, flow);
 152        if (--flow->ref)
 153                return;
 154        pr_debug("atm_tc_put: destroying\n");
 155        for (prev = &p->flows; *prev; prev = &(*prev)->next)
 156                if (*prev == flow)
 157                        break;
 158        if (!*prev) {
 159                printk(KERN_CRIT "atm_tc_put: class %p not found\n", flow);
 160                return;
 161        }
 162        *prev = flow->next;
 163        pr_debug("atm_tc_put: qdisc %p\n", flow->q);
 164        qdisc_destroy(flow->q);
 165        tcf_destroy_chain(&flow->filter_list);
 166        if (flow->sock) {
 167                pr_debug("atm_tc_put: f_count %ld\n",
 168                        file_count(flow->sock->file));
 169                flow->vcc->pop = flow->old_pop;
 170                sockfd_put(flow->sock);
 171        }
 172        if (flow->excess)
 173                atm_tc_put(sch, (unsigned long)flow->excess);
 174        if (flow != &p->link)
 175                kfree(flow);
 176        /*
 177         * If flow == &p->link, the qdisc no longer works at this point and
 178         * needs to be removed. (By the caller of atm_tc_put.)
 179         */
 180}
 181
 182static void sch_atm_pop(struct atm_vcc *vcc, struct sk_buff *skb)
 183{
 184        struct atm_qdisc_data *p = VCC2FLOW(vcc)->parent;
 185
 186        pr_debug("sch_atm_pop(vcc %p,skb %p,[qdisc %p])\n", vcc, skb, p);
 187        VCC2FLOW(vcc)->old_pop(vcc, skb);
 188        tasklet_schedule(&p->task);
 189}
 190
 191static const u8 llc_oui_ip[] = {
 192        0xaa,                   /* DSAP: non-ISO */
 193        0xaa,                   /* SSAP: non-ISO */
 194        0x03,                   /* Ctrl: Unnumbered Information Command PDU */
 195        0x00,                   /* OUI: EtherType */
 196        0x00, 0x00,
 197        0x08, 0x00
 198};                              /* Ethertype IP (0800) */
 199
 200static const struct nla_policy atm_policy[TCA_ATM_MAX + 1] = {
 201        [TCA_ATM_FD]            = { .type = NLA_U32 },
 202        [TCA_ATM_EXCESS]        = { .type = NLA_U32 },
 203};
 204
 205static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent,
 206                         struct nlattr **tca, unsigned long *arg)
 207{
 208        struct atm_qdisc_data *p = qdisc_priv(sch);
 209        struct atm_flow_data *flow = (struct atm_flow_data *)*arg;
 210        struct atm_flow_data *excess = NULL;
 211        struct nlattr *opt = tca[TCA_OPTIONS];
 212        struct nlattr *tb[TCA_ATM_MAX + 1];
 213        struct socket *sock;
 214        int fd, error, hdr_len;
 215        void *hdr;
 216
 217        pr_debug("atm_tc_change(sch %p,[qdisc %p],classid %x,parent %x,"
 218                "flow %p,opt %p)\n", sch, p, classid, parent, flow, opt);
 219        /*
 220         * The concept of parents doesn't apply for this qdisc.
 221         */
 222        if (parent && parent != TC_H_ROOT && parent != sch->handle)
 223                return -EINVAL;
 224        /*
 225         * ATM classes cannot be changed. In order to change properties of the
 226         * ATM connection, that socket needs to be modified directly (via the
 227         * native ATM API. In order to send a flow to a different VC, the old
 228         * class needs to be removed and a new one added. (This may be changed
 229         * later.)
 230         */
 231        if (flow)
 232                return -EBUSY;
 233        if (opt == NULL)
 234                return -EINVAL;
 235
 236        error = nla_parse_nested(tb, TCA_ATM_MAX, opt, atm_policy);
 237        if (error < 0)
 238                return error;
 239
 240        if (!tb[TCA_ATM_FD])
 241                return -EINVAL;
 242        fd = nla_get_u32(tb[TCA_ATM_FD]);
 243        pr_debug("atm_tc_change: fd %d\n", fd);
 244        if (tb[TCA_ATM_HDR]) {
 245                hdr_len = nla_len(tb[TCA_ATM_HDR]);
 246                hdr = nla_data(tb[TCA_ATM_HDR]);
 247        } else {
 248                hdr_len = RFC1483LLC_LEN;
 249                hdr = NULL;     /* default LLC/SNAP for IP */
 250        }
 251        if (!tb[TCA_ATM_EXCESS])
 252                excess = NULL;
 253        else {
 254                excess = (struct atm_flow_data *)
 255                        atm_tc_get(sch, nla_get_u32(tb[TCA_ATM_EXCESS]));
 256                if (!excess)
 257                        return -ENOENT;
 258        }
 259        pr_debug("atm_tc_change: type %d, payload %d, hdr_len %d\n",
 260                 opt->nla_type, nla_len(opt), hdr_len);
 261        sock = sockfd_lookup(fd, &error);
 262        if (!sock)
 263                return error;   /* f_count++ */
 264        pr_debug("atm_tc_change: f_count %ld\n", file_count(sock->file));
 265        if (sock->ops->family != PF_ATMSVC && sock->ops->family != PF_ATMPVC) {
 266                error = -EPROTOTYPE;
 267                goto err_out;
 268        }
 269        /* @@@ should check if the socket is really operational or we'll crash
 270           on vcc->send */
 271        if (classid) {
 272                if (TC_H_MAJ(classid ^ sch->handle)) {
 273                        pr_debug("atm_tc_change: classid mismatch\n");
 274                        error = -EINVAL;
 275                        goto err_out;
 276                }
 277                if (find_flow(p, flow)) {
 278                        error = -EEXIST;
 279                        goto err_out;
 280                }
 281        } else {
 282                int i;
 283                unsigned long cl;
 284
 285                for (i = 1; i < 0x8000; i++) {
 286                        classid = TC_H_MAKE(sch->handle, 0x8000 | i);
 287                        cl = atm_tc_get(sch, classid);
 288                        if (!cl)
 289                                break;
 290                        atm_tc_put(sch, cl);
 291                }
 292        }
 293        pr_debug("atm_tc_change: new id %x\n", classid);
 294        flow = kzalloc(sizeof(struct atm_flow_data) + hdr_len, GFP_KERNEL);
 295        pr_debug("atm_tc_change: flow %p\n", flow);
 296        if (!flow) {
 297                error = -ENOBUFS;
 298                goto err_out;
 299        }
 300        flow->filter_list = NULL;
 301        flow->q = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue,
 302                                    &pfifo_qdisc_ops, classid);
 303        if (!flow->q)
 304                flow->q = &noop_qdisc;
 305        pr_debug("atm_tc_change: qdisc %p\n", flow->q);
 306        flow->sock = sock;
 307        flow->vcc = ATM_SD(sock);       /* speedup */
 308        flow->vcc->user_back = flow;
 309        pr_debug("atm_tc_change: vcc %p\n", flow->vcc);
 310        flow->old_pop = flow->vcc->pop;
 311        flow->parent = p;
 312        flow->vcc->pop = sch_atm_pop;
 313        flow->classid = classid;
 314        flow->ref = 1;
 315        flow->excess = excess;
 316        flow->next = p->link.next;
 317        p->link.next = flow;
 318        flow->hdr_len = hdr_len;
 319        if (hdr)
 320                memcpy(flow->hdr, hdr, hdr_len);
 321        else
 322                memcpy(flow->hdr, llc_oui_ip, sizeof(llc_oui_ip));
 323        *arg = (unsigned long)flow;
 324        return 0;
 325err_out:
 326        if (excess)
 327                atm_tc_put(sch, (unsigned long)excess);
 328        sockfd_put(sock);
 329        return error;
 330}
 331
 332static int atm_tc_delete(struct Qdisc *sch, unsigned long arg)
 333{
 334        struct atm_qdisc_data *p = qdisc_priv(sch);
 335        struct atm_flow_data *flow = (struct atm_flow_data *)arg;
 336
 337        pr_debug("atm_tc_delete(sch %p,[qdisc %p],flow %p)\n", sch, p, flow);
 338        if (!find_flow(qdisc_priv(sch), flow))
 339                return -EINVAL;
 340        if (flow->filter_list || flow == &p->link)
 341                return -EBUSY;
 342        /*
 343         * Reference count must be 2: one for "keepalive" (set at class
 344         * creation), and one for the reference held when calling delete.
 345         */
 346        if (flow->ref < 2) {
 347                printk(KERN_ERR "atm_tc_delete: flow->ref == %d\n", flow->ref);
 348                return -EINVAL;
 349        }
 350        if (flow->ref > 2)
 351                return -EBUSY;  /* catch references via excess, etc. */
 352        atm_tc_put(sch, arg);
 353        return 0;
 354}
 355
 356static void atm_tc_walk(struct Qdisc *sch, struct qdisc_walker *walker)
 357{
 358        struct atm_qdisc_data *p = qdisc_priv(sch);
 359        struct atm_flow_data *flow;
 360
 361        pr_debug("atm_tc_walk(sch %p,[qdisc %p],walker %p)\n", sch, p, walker);
 362        if (walker->stop)
 363                return;
 364        for (flow = p->flows; flow; flow = flow->next) {
 365                if (walker->count >= walker->skip)
 366                        if (walker->fn(sch, (unsigned long)flow, walker) < 0) {
 367                                walker->stop = 1;
 368                                break;
 369                        }
 370                walker->count++;
 371        }
 372}
 373
 374static struct tcf_proto **atm_tc_find_tcf(struct Qdisc *sch, unsigned long cl)
 375{
 376        struct atm_qdisc_data *p = qdisc_priv(sch);
 377        struct atm_flow_data *flow = (struct atm_flow_data *)cl;
 378
 379        pr_debug("atm_tc_find_tcf(sch %p,[qdisc %p],flow %p)\n", sch, p, flow);
 380        return flow ? &flow->filter_list : &p->link.filter_list;
 381}
 382
 383/* --------------------------- Qdisc operations ---------------------------- */
 384
 385static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 386{
 387        struct atm_qdisc_data *p = qdisc_priv(sch);
 388        struct atm_flow_data *flow = NULL;      /* @@@ */
 389        struct tcf_result res;
 390        int result;
 391        int ret = NET_XMIT_POLICED;
 392
 393        pr_debug("atm_tc_enqueue(skb %p,sch %p,[qdisc %p])\n", skb, sch, p);
 394        result = TC_POLICE_OK;  /* be nice to gcc */
 395        if (TC_H_MAJ(skb->priority) != sch->handle ||
 396            !(flow = (struct atm_flow_data *)atm_tc_get(sch, skb->priority)))
 397                for (flow = p->flows; flow; flow = flow->next)
 398                        if (flow->filter_list) {
 399                                result = tc_classify_compat(skb,
 400                                                            flow->filter_list,
 401                                                            &res);
 402                                if (result < 0)
 403                                        continue;
 404                                flow = (struct atm_flow_data *)res.class;
 405                                if (!flow)
 406                                        flow = lookup_flow(sch, res.classid);
 407                                break;
 408                        }
 409        if (!flow)
 410                flow = &p->link;
 411        else {
 412                if (flow->vcc)
 413                        ATM_SKB(skb)->atm_options = flow->vcc->atm_options;
 414                /*@@@ looks good ... but it's not supposed to work :-) */
 415#ifdef CONFIG_NET_CLS_ACT
 416                switch (result) {
 417                case TC_ACT_QUEUED:
 418                case TC_ACT_STOLEN:
 419                        kfree_skb(skb);
 420                        return NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
 421                case TC_ACT_SHOT:
 422                        kfree_skb(skb);
 423                        goto drop;
 424                case TC_POLICE_RECLASSIFY:
 425                        if (flow->excess)
 426                                flow = flow->excess;
 427                        else
 428                                ATM_SKB(skb)->atm_options |= ATM_ATMOPT_CLP;
 429                        break;
 430                }
 431#endif
 432        }
 433
 434        ret = qdisc_enqueue(skb, flow->q);
 435        if (ret != 0) {
 436drop: __maybe_unused
 437                if (net_xmit_drop_count(ret)) {
 438                        sch->qstats.drops++;
 439                        if (flow)
 440                                flow->qstats.drops++;
 441                }
 442                return ret;
 443        }
 444        sch->bstats.bytes += qdisc_pkt_len(skb);
 445        sch->bstats.packets++;
 446        flow->bstats.bytes += qdisc_pkt_len(skb);
 447        flow->bstats.packets++;
 448        /*
 449         * Okay, this may seem weird. We pretend we've dropped the packet if
 450         * it goes via ATM. The reason for this is that the outer qdisc
 451         * expects to be able to q->dequeue the packet later on if we return
 452         * success at this place. Also, sch->q.qdisc needs to reflect whether
 453         * there is a packet egligible for dequeuing or not. Note that the
 454         * statistics of the outer qdisc are necessarily wrong because of all
 455         * this. There's currently no correct solution for this.
 456         */
 457        if (flow == &p->link) {
 458                sch->q.qlen++;
 459                return 0;
 460        }
 461        tasklet_schedule(&p->task);
 462        return NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
 463}
 464
 465/*
 466 * Dequeue packets and send them over ATM. Note that we quite deliberately
 467 * avoid checking net_device's flow control here, simply because sch_atm
 468 * uses its own channels, which have nothing to do with any CLIP/LANE/or
 469 * non-ATM interfaces.
 470 */
 471
 472static void sch_atm_dequeue(unsigned long data)
 473{
 474        struct Qdisc *sch = (struct Qdisc *)data;
 475        struct atm_qdisc_data *p = qdisc_priv(sch);
 476        struct atm_flow_data *flow;
 477        struct sk_buff *skb;
 478
 479        pr_debug("sch_atm_dequeue(sch %p,[qdisc %p])\n", sch, p);
 480        for (flow = p->link.next; flow; flow = flow->next)
 481                /*
 482                 * If traffic is properly shaped, this won't generate nasty
 483                 * little bursts. Otherwise, it may ... (but that's okay)
 484                 */
 485                while ((skb = flow->q->ops->peek(flow->q))) {
 486                        if (!atm_may_send(flow->vcc, skb->truesize))
 487                                break;
 488
 489                        skb = qdisc_dequeue_peeked(flow->q);
 490                        if (unlikely(!skb))
 491                                break;
 492
 493                        pr_debug("atm_tc_dequeue: sending on class %p\n", flow);
 494                        /* remove any LL header somebody else has attached */
 495                        skb_pull(skb, skb_network_offset(skb));
 496                        if (skb_headroom(skb) < flow->hdr_len) {
 497                                struct sk_buff *new;
 498
 499                                new = skb_realloc_headroom(skb, flow->hdr_len);
 500                                dev_kfree_skb(skb);
 501                                if (!new)
 502                                        continue;
 503                                skb = new;
 504                        }
 505                        pr_debug("sch_atm_dequeue: ip %p, data %p\n",
 506                                 skb_network_header(skb), skb->data);
 507                        ATM_SKB(skb)->vcc = flow->vcc;
 508                        memcpy(skb_push(skb, flow->hdr_len), flow->hdr,
 509                               flow->hdr_len);
 510                        atomic_add(skb->truesize,
 511                                   &sk_atm(flow->vcc)->sk_wmem_alloc);
 512                        /* atm.atm_options are already set by atm_tc_enqueue */
 513                        flow->vcc->send(flow->vcc, skb);
 514                }
 515}
 516
 517static struct sk_buff *atm_tc_dequeue(struct Qdisc *sch)
 518{
 519        struct atm_qdisc_data *p = qdisc_priv(sch);
 520        struct sk_buff *skb;
 521
 522        pr_debug("atm_tc_dequeue(sch %p,[qdisc %p])\n", sch, p);
 523        tasklet_schedule(&p->task);
 524        skb = qdisc_dequeue_peeked(p->link.q);
 525        if (skb)
 526                sch->q.qlen--;
 527        return skb;
 528}
 529
 530static struct sk_buff *atm_tc_peek(struct Qdisc *sch)
 531{
 532        struct atm_qdisc_data *p = qdisc_priv(sch);
 533
 534        pr_debug("atm_tc_peek(sch %p,[qdisc %p])\n", sch, p);
 535
 536        return p->link.q->ops->peek(p->link.q);
 537}
 538
 539static unsigned int atm_tc_drop(struct Qdisc *sch)
 540{
 541        struct atm_qdisc_data *p = qdisc_priv(sch);
 542        struct atm_flow_data *flow;
 543        unsigned int len;
 544
 545        pr_debug("atm_tc_drop(sch %p,[qdisc %p])\n", sch, p);
 546        for (flow = p->flows; flow; flow = flow->next)
 547                if (flow->q->ops->drop && (len = flow->q->ops->drop(flow->q)))
 548                        return len;
 549        return 0;
 550}
 551
 552static int atm_tc_init(struct Qdisc *sch, struct nlattr *opt)
 553{
 554        struct atm_qdisc_data *p = qdisc_priv(sch);
 555
 556        pr_debug("atm_tc_init(sch %p,[qdisc %p],opt %p)\n", sch, p, opt);
 557        p->flows = &p->link;
 558        p->link.q = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue,
 559                                      &pfifo_qdisc_ops, sch->handle);
 560        if (!p->link.q)
 561                p->link.q = &noop_qdisc;
 562        pr_debug("atm_tc_init: link (%p) qdisc %p\n", &p->link, p->link.q);
 563        p->link.filter_list = NULL;
 564        p->link.vcc = NULL;
 565        p->link.sock = NULL;
 566        p->link.classid = sch->handle;
 567        p->link.ref = 1;
 568        p->link.next = NULL;
 569        tasklet_init(&p->task, sch_atm_dequeue, (unsigned long)sch);
 570        return 0;
 571}
 572
 573static void atm_tc_reset(struct Qdisc *sch)
 574{
 575        struct atm_qdisc_data *p = qdisc_priv(sch);
 576        struct atm_flow_data *flow;
 577
 578        pr_debug("atm_tc_reset(sch %p,[qdisc %p])\n", sch, p);
 579        for (flow = p->flows; flow; flow = flow->next)
 580                qdisc_reset(flow->q);
 581        sch->q.qlen = 0;
 582}
 583
 584static void atm_tc_destroy(struct Qdisc *sch)
 585{
 586        struct atm_qdisc_data *p = qdisc_priv(sch);
 587        struct atm_flow_data *flow;
 588
 589        pr_debug("atm_tc_destroy(sch %p,[qdisc %p])\n", sch, p);
 590        for (flow = p->flows; flow; flow = flow->next)
 591                tcf_destroy_chain(&flow->filter_list);
 592
 593        /* races ? */
 594        while ((flow = p->flows)) {
 595                if (flow->ref > 1)
 596                        printk(KERN_ERR "atm_destroy: %p->ref = %d\n", flow,
 597                               flow->ref);
 598                atm_tc_put(sch, (unsigned long)flow);
 599                if (p->flows == flow) {
 600                        printk(KERN_ERR "atm_destroy: putting flow %p didn't "
 601                               "kill it\n", flow);
 602                        p->flows = flow->next;  /* brute force */
 603                        break;
 604                }
 605        }
 606        tasklet_kill(&p->task);
 607}
 608
 609static int atm_tc_dump_class(struct Qdisc *sch, unsigned long cl,
 610                             struct sk_buff *skb, struct tcmsg *tcm)
 611{
 612        struct atm_qdisc_data *p = qdisc_priv(sch);
 613        struct atm_flow_data *flow = (struct atm_flow_data *)cl;
 614        struct nlattr *nest;
 615
 616        pr_debug("atm_tc_dump_class(sch %p,[qdisc %p],flow %p,skb %p,tcm %p)\n",
 617                sch, p, flow, skb, tcm);
 618        if (!find_flow(p, flow))
 619                return -EINVAL;
 620        tcm->tcm_handle = flow->classid;
 621        tcm->tcm_info = flow->q->handle;
 622
 623        nest = nla_nest_start(skb, TCA_OPTIONS);
 624        if (nest == NULL)
 625                goto nla_put_failure;
 626
 627        NLA_PUT(skb, TCA_ATM_HDR, flow->hdr_len, flow->hdr);
 628        if (flow->vcc) {
 629                struct sockaddr_atmpvc pvc;
 630                int state;
 631
 632                pvc.sap_family = AF_ATMPVC;
 633                pvc.sap_addr.itf = flow->vcc->dev ? flow->vcc->dev->number : -1;
 634                pvc.sap_addr.vpi = flow->vcc->vpi;
 635                pvc.sap_addr.vci = flow->vcc->vci;
 636                NLA_PUT(skb, TCA_ATM_ADDR, sizeof(pvc), &pvc);
 637                state = ATM_VF2VS(flow->vcc->flags);
 638                NLA_PUT_U32(skb, TCA_ATM_STATE, state);
 639        }
 640        if (flow->excess)
 641                NLA_PUT_U32(skb, TCA_ATM_EXCESS, flow->classid);
 642        else {
 643                NLA_PUT_U32(skb, TCA_ATM_EXCESS, 0);
 644        }
 645
 646        nla_nest_end(skb, nest);
 647        return skb->len;
 648
 649nla_put_failure:
 650        nla_nest_cancel(skb, nest);
 651        return -1;
 652}
 653static int
 654atm_tc_dump_class_stats(struct Qdisc *sch, unsigned long arg,
 655                        struct gnet_dump *d)
 656{
 657        struct atm_flow_data *flow = (struct atm_flow_data *)arg;
 658
 659        flow->qstats.qlen = flow->q->q.qlen;
 660
 661        if (gnet_stats_copy_basic(d, &flow->bstats) < 0 ||
 662            gnet_stats_copy_queue(d, &flow->qstats) < 0)
 663                return -1;
 664
 665        return 0;
 666}
 667
 668static int atm_tc_dump(struct Qdisc *sch, struct sk_buff *skb)
 669{
 670        return 0;
 671}
 672
 673static const struct Qdisc_class_ops atm_class_ops = {
 674        .graft          = atm_tc_graft,
 675        .leaf           = atm_tc_leaf,
 676        .get            = atm_tc_get,
 677        .put            = atm_tc_put,
 678        .change         = atm_tc_change,
 679        .delete         = atm_tc_delete,
 680        .walk           = atm_tc_walk,
 681        .tcf_chain      = atm_tc_find_tcf,
 682        .bind_tcf       = atm_tc_bind_filter,
 683        .unbind_tcf     = atm_tc_put,
 684        .dump           = atm_tc_dump_class,
 685        .dump_stats     = atm_tc_dump_class_stats,
 686};
 687
 688static struct Qdisc_ops atm_qdisc_ops __read_mostly = {
 689        .cl_ops         = &atm_class_ops,
 690        .id             = "atm",
 691        .priv_size      = sizeof(struct atm_qdisc_data),
 692        .enqueue        = atm_tc_enqueue,
 693        .dequeue        = atm_tc_dequeue,
 694        .peek           = atm_tc_peek,
 695        .drop           = atm_tc_drop,
 696        .init           = atm_tc_init,
 697        .reset          = atm_tc_reset,
 698        .destroy        = atm_tc_destroy,
 699        .dump           = atm_tc_dump,
 700        .owner          = THIS_MODULE,
 701};
 702
 703static int __init atm_init(void)
 704{
 705        return register_qdisc(&atm_qdisc_ops);
 706}
 707
 708static void __exit atm_exit(void)
 709{
 710        unregister_qdisc(&atm_qdisc_ops);
 711}
 712
 713module_init(atm_init)
 714module_exit(atm_exit)
 715MODULE_LICENSE("GPL");
 716
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.