Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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