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