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