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 cba767175becadc5c4016cceb7bfdd2c7fe722f4 718 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 %ld\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 %ld\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(qdisc_dev(sch), sch->dev_queue, 300 &pfifo_qdisc_ops, classid); 301 if (!flow->q) 302 flow->q = &noop_qdisc; 303 pr_debug("atm_tc_change: qdisc %p\n", flow->q); 304 flow->sock = sock; 305 flow->vcc = ATM_SD(sock); /* speedup */ 306 flow->vcc->user_back = flow; 307 pr_debug("atm_tc_change: vcc %p\n", flow->vcc); 308 flow->old_pop = flow->vcc->pop; 309 flow->parent = p; 310 flow->vcc->pop = sch_atm_pop; 311 flow->classid = classid; 312 flow->ref = 1; 313 flow->excess = excess; 314 flow->next = p->link.next; 315 p->link.next = flow; 316 flow->hdr_len = hdr_len; 317 if (hdr) 318 memcpy(flow->hdr, hdr, hdr_len); 319 else 320 memcpy(flow->hdr, llc_oui_ip, sizeof(llc_oui_ip)); 321 *arg = (unsigned long)flow; 322 return 0; 323err_out: 324 if (excess) 325 atm_tc_put(sch, (unsigned long)excess); 326 sockfd_put(sock); 327 return error; 328} 329 330static int atm_tc_delete(struct Qdisc *sch, unsigned long arg) 331{ 332 struct atm_qdisc_data *p = qdisc_priv(sch); 333 struct atm_flow_data *flow = (struct atm_flow_data *)arg; 334 335 pr_debug("atm_tc_delete(sch %p,[qdisc %p],flow %p)\n", sch, p, flow); 336 if (!find_flow(qdisc_priv(sch), flow)) 337 return -EINVAL; 338 if (flow->filter_list || flow == &p->link) 339 return -EBUSY; 340 /* 341 * Reference count must be 2: one for "keepalive" (set at class 342 * creation), and one for the reference held when calling delete. 343 */ 344 if (flow->ref < 2) { 345 printk(KERN_ERR "atm_tc_delete: flow->ref == %d\n", flow->ref); 346 return -EINVAL; 347 } 348 if (flow->ref > 2) 349 return -EBUSY; /* catch references via excess, etc. */ 350 atm_tc_put(sch, arg); 351 return 0; 352} 353 354static void atm_tc_walk(struct Qdisc *sch, struct qdisc_walker *walker) 355{ 356 struct atm_qdisc_data *p = qdisc_priv(sch); 357 struct atm_flow_data *flow; 358 359 pr_debug("atm_tc_walk(sch %p,[qdisc %p],walker %p)\n", sch, p, walker); 360 if (walker->stop) 361 return; 362 for (flow = p->flows; flow; flow = flow->next) { 363 if (walker->count >= walker->skip) 364 if (walker->fn(sch, (unsigned long)flow, walker) < 0) { 365 walker->stop = 1; 366 break; 367 } 368 walker->count++; 369 } 370} 371 372static struct tcf_proto **atm_tc_find_tcf(struct Qdisc *sch, unsigned long cl) 373{ 374 struct atm_qdisc_data *p = qdisc_priv(sch); 375 struct atm_flow_data *flow = (struct atm_flow_data *)cl; 376 377 pr_debug("atm_tc_find_tcf(sch %p,[qdisc %p],flow %p)\n", sch, p, flow); 378 return flow ? &flow->filter_list : &p->link.filter_list; 379} 380 381/* --------------------------- Qdisc operations ---------------------------- */ 382 383static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch) 384{ 385 struct atm_qdisc_data *p = qdisc_priv(sch); 386 struct atm_flow_data *flow = NULL; /* @@@ */ 387 struct tcf_result res; 388 int result; 389 int ret = NET_XMIT_POLICED; 390 391 pr_debug("atm_tc_enqueue(skb %p,sch %p,[qdisc %p])\n", skb, sch, p); 392 result = TC_POLICE_OK; /* be nice to gcc */ 393 if (TC_H_MAJ(skb->priority) != sch->handle || 394 !(flow = (struct atm_flow_data *)atm_tc_get(sch, skb->priority))) 395 for (flow = p->flows; flow; flow = flow->next) 396 if (flow->filter_list) { 397 result = tc_classify_compat(skb, 398 flow->filter_list, 399 &res); 400 if (result < 0) 401 continue; 402 flow = (struct atm_flow_data *)res.class; 403 if (!flow) 404 flow = lookup_flow(sch, res.classid); 405 break; 406 } 407 if (!flow) 408 flow = &p->link; 409 else { 410 if (flow->vcc) 411 ATM_SKB(skb)->atm_options = flow->vcc->atm_options; 412 /*@@@ looks good ... but it's not supposed to work :-) */ 413#ifdef CONFIG_NET_CLS_ACT 414 switch (result) { 415 case TC_ACT_QUEUED: 416 case TC_ACT_STOLEN: 417 kfree_skb(skb); 418 return NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; 419 case TC_ACT_SHOT: 420 kfree_skb(skb); 421 goto drop; 422 case TC_POLICE_RECLASSIFY: 423 if (flow->excess) 424 flow = flow->excess; 425 else 426 ATM_SKB(skb)->atm_options |= ATM_ATMOPT_CLP; 427 break; 428 } 429#endif 430 } 431 432 ret = qdisc_enqueue(skb, flow->q); 433 if (ret != 0) { 434drop: __maybe_unused 435 if (net_xmit_drop_count(ret)) { 436 sch->qstats.drops++; 437 if (flow) 438 flow->qstats.drops++; 439 } 440 return ret; 441 } 442 sch->bstats.bytes += qdisc_pkt_len(skb); 443 sch->bstats.packets++; 444 flow->bstats.bytes += qdisc_pkt_len(skb); 445 flow->bstats.packets++; 446 /* 447 * Okay, this may seem weird. We pretend we've dropped the packet if 448 * it goes via ATM. The reason for this is that the outer qdisc 449 * expects to be able to q->dequeue the packet later on if we return 450 * success at this place. Also, sch->q.qdisc needs to reflect whether 451 * there is a packet egligible for dequeuing or not. Note that the 452 * statistics of the outer qdisc are necessarily wrong because of all 453 * this. There's currently no correct solution for this. 454 */ 455 if (flow == &p->link) { 456 sch->q.qlen++; 457 return 0; 458 } 459 tasklet_schedule(&p->task); 460 return NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; 461} 462 463/* 464 * Dequeue packets and send them over ATM. Note that we quite deliberately 465 * avoid checking net_device's flow control here, simply because sch_atm 466 * uses its own channels, which have nothing to do with any CLIP/LANE/or 467 * non-ATM interfaces. 468 */ 469 470static void sch_atm_dequeue(unsigned long data) 471{ 472 struct Qdisc *sch = (struct Qdisc *)data; 473 struct atm_qdisc_data *p = qdisc_priv(sch); 474 struct atm_flow_data *flow; 475 struct sk_buff *skb; 476 477 pr_debug("sch_atm_dequeue(sch %p,[qdisc %p])\n", sch, p); 478 for (flow = p->link.next; flow; flow = flow->next) 479 /* 480 * If traffic is properly shaped, this won't generate nasty 481 * little bursts. Otherwise, it may ... (but that's okay) 482 */ 483 while ((skb = flow->q->dequeue(flow->q))) { 484 if (!atm_may_send(flow->vcc, skb->truesize)) { 485 (void)flow->q->ops->requeue(skb, flow->q); 486 break; 487 } 488 pr_debug("atm_tc_dequeue: sending on class %p\n", flow); 489 /* remove any LL header somebody else has attached */ 490 skb_pull(skb, skb_network_offset(skb)); 491 if (skb_headroom(skb) < flow->hdr_len) { 492 struct sk_buff *new; 493 494 new = skb_realloc_headroom(skb, flow->hdr_len); 495 dev_kfree_skb(skb); 496 if (!new) 497 continue; 498 skb = new; 499 } 500 pr_debug("sch_atm_dequeue: ip %p, data %p\n", 501 skb_network_header(skb), skb->data); 502 ATM_SKB(skb)->vcc = flow->vcc; 503 memcpy(skb_push(skb, flow->hdr_len), flow->hdr, 504 flow->hdr_len); 505 atomic_add(skb->truesize, 506 &sk_atm(flow->vcc)->sk_wmem_alloc); 507 /* atm.atm_options are already set by atm_tc_enqueue */ 508 flow->vcc->send(flow->vcc, skb); 509 } 510} 511 512static struct sk_buff *atm_tc_dequeue(struct Qdisc *sch) 513{ 514 struct atm_qdisc_data *p = qdisc_priv(sch); 515 struct sk_buff *skb; 516 517 pr_debug("atm_tc_dequeue(sch %p,[qdisc %p])\n", sch, p); 518 tasklet_schedule(&p->task); 519 skb = p->link.q->dequeue(p->link.q); 520 if (skb) 521 sch->q.qlen--; 522 return skb; 523} 524 525static int atm_tc_requeue(struct sk_buff *skb, struct Qdisc *sch) 526{ 527 struct atm_qdisc_data *p = qdisc_priv(sch); 528 int ret; 529 530 pr_debug("atm_tc_requeue(skb %p,sch %p,[qdisc %p])\n", skb, sch, p); 531 ret = p->link.q->ops->requeue(skb, p->link.q); 532 if (!ret) { 533 sch->q.qlen++; 534 sch->qstats.requeues++; 535 } else if (net_xmit_drop_count(ret)) { 536 sch->qstats.drops++; 537 p->link.qstats.drops++; 538 } 539 return ret; 540} 541 542static unsigned int atm_tc_drop(struct Qdisc *sch) 543{ 544 struct atm_qdisc_data *p = qdisc_priv(sch); 545 struct atm_flow_data *flow; 546 unsigned int len; 547 548 pr_debug("atm_tc_drop(sch %p,[qdisc %p])\n", sch, p); 549 for (flow = p->flows; flow; flow = flow->next) 550 if (flow->q->ops->drop && (len = flow->q->ops->drop(flow->q))) 551 return len; 552 return 0; 553} 554 555static int atm_tc_init(struct Qdisc *sch, struct nlattr *opt) 556{ 557 struct atm_qdisc_data *p = qdisc_priv(sch); 558 559 pr_debug("atm_tc_init(sch %p,[qdisc %p],opt %p)\n", sch, p, opt); 560 p->flows = &p->link; 561 p->link.q = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue, 562 &pfifo_qdisc_ops, sch->handle); 563 if (!p->link.q) 564 p->link.q = &noop_qdisc; 565 pr_debug("atm_tc_init: link (%p) qdisc %p\n", &p->link, p->link.q); 566 p->link.filter_list = NULL; 567 p->link.vcc = NULL; 568 p->link.sock = NULL; 569 p->link.classid = sch->handle; 570 p->link.ref = 1; 571 p->link.next = NULL; 572 tasklet_init(&p->task, sch_atm_dequeue, (unsigned long)sch); 573 return 0; 574} 575 576static void atm_tc_reset(struct Qdisc *sch) 577{ 578 struct atm_qdisc_data *p = qdisc_priv(sch); 579 struct atm_flow_data *flow; 580 581 pr_debug("atm_tc_reset(sch %p,[qdisc %p])\n", sch, p); 582 for (flow = p->flows; flow; flow = flow->next) 583 qdisc_reset(flow->q); 584 sch->q.qlen = 0; 585} 586 587static void atm_tc_destroy(struct Qdisc *sch) 588{ 589 struct atm_qdisc_data *p = qdisc_priv(sch); 590 struct atm_flow_data *flow; 591 592 pr_debug("atm_tc_destroy(sch %p,[qdisc %p])\n", sch, p); 593 for (flow = p->flows; flow; flow = flow->next) 594 tcf_destroy_chain(&flow->filter_list); 595 596 /* races ? */ 597 while ((flow = p->flows)) { 598 if (flow->ref > 1) 599 printk(KERN_ERR "atm_destroy: %p->ref = %d\n", flow, 600 flow->ref); 601 atm_tc_put(sch, (unsigned long)flow); 602 if (p->flows == flow) { 603 printk(KERN_ERR "atm_destroy: putting flow %p didn't " 604 "kill it\n", flow); 605 p->flows = flow->next; /* brute force */ 606 break; 607 } 608 } 609 tasklet_kill(&p->task); 610} 611 612static int atm_tc_dump_class(struct Qdisc *sch, unsigned long cl, 613 struct sk_buff *skb, struct tcmsg *tcm) 614{ 615 struct atm_qdisc_data *p = qdisc_priv(sch); 616 struct atm_flow_data *flow = (struct atm_flow_data *)cl; 617 struct nlattr *nest; 618 619 pr_debug("atm_tc_dump_class(sch %p,[qdisc %p],flow %p,skb %p,tcm %p)\n", 620 sch, p, flow, skb, tcm); 621 if (!find_flow(p, flow)) 622 return -EINVAL; 623 tcm->tcm_handle = flow->classid; 624 tcm->tcm_info = flow->q->handle; 625 626 nest = nla_nest_start(skb, TCA_OPTIONS); 627 if (nest == NULL) 628 goto nla_put_failure; 629 630 NLA_PUT(skb, TCA_ATM_HDR, flow->hdr_len, flow->hdr); 631 if (flow->vcc) { 632 struct sockaddr_atmpvc pvc; 633 int state; 634 635 pvc.sap_family = AF_ATMPVC; 636 pvc.sap_addr.itf = flow->vcc->dev ? flow->vcc->dev->number : -1; 637 pvc.sap_addr.vpi = flow->vcc->vpi; 638 pvc.sap_addr.vci = flow->vcc->vci; 639 NLA_PUT(skb, TCA_ATM_ADDR, sizeof(pvc), &pvc); 640 state = ATM_VF2VS(flow->vcc->flags); 641 NLA_PUT_U32(skb, TCA_ATM_STATE, state); 642 } 643 if (flow->excess) 644 NLA_PUT_U32(skb, TCA_ATM_EXCESS, flow->classid); 645 else { 646 NLA_PUT_U32(skb, TCA_ATM_EXCESS, 0); 647 } 648 649 nla_nest_end(skb, nest); 650 return skb->len; 651 652nla_put_failure: 653 nla_nest_cancel(skb, nest); 654 return -1; 655} 656static int 657atm_tc_dump_class_stats(struct Qdisc *sch, unsigned long arg, 658 struct gnet_dump *d) 659{ 660 struct atm_flow_data *flow = (struct atm_flow_data *)arg; 661 662 flow->qstats.qlen = flow->q->q.qlen; 663 664 if (gnet_stats_copy_basic(d, &flow->bstats) < 0 || 665 gnet_stats_copy_queue(d, &flow->qstats) < 0) 666 return -1; 667 668 return 0; 669} 670 671static int atm_tc_dump(struct Qdisc *sch, struct sk_buff *skb) 672{ 673 return 0; 674} 675 676static const struct Qdisc_class_ops atm_class_ops = { 677 .graft = atm_tc_graft, 678 .leaf = atm_tc_leaf, 679 .get = atm_tc_get, 680 .put = atm_tc_put, 681 .change = atm_tc_change, 682 .delete = atm_tc_delete, 683 .walk = atm_tc_walk, 684 .tcf_chain = atm_tc_find_tcf, 685 .bind_tcf = atm_tc_bind_filter, 686 .unbind_tcf = atm_tc_put, 687 .dump = atm_tc_dump_class, 688 .dump_stats = atm_tc_dump_class_stats, 689}; 690 691static struct Qdisc_ops atm_qdisc_ops __read_mostly = { 692 .cl_ops = &atm_class_ops, 693 .id = "atm", 694 .priv_size = sizeof(struct atm_qdisc_data), 695 .enqueue = atm_tc_enqueue, 696 .dequeue = atm_tc_dequeue, 697 .requeue = atm_tc_requeue, 698 .drop = atm_tc_drop, 699 .init = atm_tc_init, 700 .reset = atm_tc_reset, 701 .destroy = atm_tc_destroy, 702 .dump = atm_tc_dump, 703 .owner = THIS_MODULE, 704}; 705 706static int __init atm_init(void) 707{ 708 return register_qdisc(&atm_qdisc_ops); 709} 710 711static void __exit atm_exit(void) 712{ 713 unregister_qdisc(&atm_qdisc_ops); 714} 715 716module_init(atm_init) 717module_exit(atm_exit) 718MODULE_LICENSE("GPL");