diff options
Diffstat (limited to 'net/sched/sch_atm.c')
-rw-r--r-- | net/sched/sch_atm.c | 98 |
1 files changed, 41 insertions, 57 deletions
diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index fcbb86a486a2..e114f23d5eae 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c | |||
@@ -52,7 +52,7 @@ struct atm_flow_data { | |||
52 | int ref; /* reference count */ | 52 | int ref; /* reference count */ |
53 | struct gnet_stats_basic_packed bstats; | 53 | struct gnet_stats_basic_packed bstats; |
54 | struct gnet_stats_queue qstats; | 54 | struct gnet_stats_queue qstats; |
55 | struct atm_flow_data *next; | 55 | struct list_head list; |
56 | struct atm_flow_data *excess; /* flow for excess traffic; | 56 | struct atm_flow_data *excess; /* flow for excess traffic; |
57 | NULL to set CLP instead */ | 57 | NULL to set CLP instead */ |
58 | int hdr_len; | 58 | int hdr_len; |
@@ -61,34 +61,23 @@ struct atm_flow_data { | |||
61 | 61 | ||
62 | struct atm_qdisc_data { | 62 | struct atm_qdisc_data { |
63 | struct atm_flow_data link; /* unclassified skbs go here */ | 63 | struct atm_flow_data link; /* unclassified skbs go here */ |
64 | struct atm_flow_data *flows; /* NB: "link" is also on this | 64 | struct list_head flows; /* NB: "link" is also on this |
65 | list */ | 65 | list */ |
66 | struct tasklet_struct task; /* dequeue tasklet */ | 66 | struct tasklet_struct task; /* dequeue tasklet */ |
67 | }; | 67 | }; |
68 | 68 | ||
69 | /* ------------------------- Class/flow operations ------------------------- */ | 69 | /* ------------------------- Class/flow operations ------------------------- */ |
70 | 70 | ||
71 | static 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 | |||
83 | static inline struct atm_flow_data *lookup_flow(struct Qdisc *sch, u32 classid) | 71 | static inline struct atm_flow_data *lookup_flow(struct Qdisc *sch, u32 classid) |
84 | { | 72 | { |
85 | struct atm_qdisc_data *p = qdisc_priv(sch); | 73 | struct atm_qdisc_data *p = qdisc_priv(sch); |
86 | struct atm_flow_data *flow; | 74 | struct atm_flow_data *flow; |
87 | 75 | ||
88 | for (flow = p->flows; flow; flow = flow->next) | 76 | list_for_each_entry(flow, &p->flows, list) { |
89 | if (flow->classid == classid) | 77 | if (flow->classid == classid) |
90 | break; | 78 | return flow; |
91 | return flow; | 79 | } |
80 | return NULL; | ||
92 | } | 81 | } |
93 | 82 | ||
94 | static int atm_tc_graft(struct Qdisc *sch, unsigned long arg, | 83 | static int atm_tc_graft(struct Qdisc *sch, unsigned long arg, |
@@ -99,7 +88,7 @@ static int atm_tc_graft(struct Qdisc *sch, unsigned long arg, | |||
99 | 88 | ||
100 | pr_debug("atm_tc_graft(sch %p,[qdisc %p],flow %p,new %p,old %p)\n", | 89 | pr_debug("atm_tc_graft(sch %p,[qdisc %p],flow %p,new %p,old %p)\n", |
101 | sch, p, flow, new, old); | 90 | sch, p, flow, new, old); |
102 | if (!find_flow(p, flow)) | 91 | if (list_empty(&flow->list)) |
103 | return -EINVAL; | 92 | return -EINVAL; |
104 | if (!new) | 93 | if (!new) |
105 | new = &noop_qdisc; | 94 | new = &noop_qdisc; |
@@ -146,20 +135,12 @@ static void atm_tc_put(struct Qdisc *sch, unsigned long cl) | |||
146 | { | 135 | { |
147 | struct atm_qdisc_data *p = qdisc_priv(sch); | 136 | struct atm_qdisc_data *p = qdisc_priv(sch); |
148 | struct atm_flow_data *flow = (struct atm_flow_data *)cl; | 137 | struct atm_flow_data *flow = (struct atm_flow_data *)cl; |
149 | struct atm_flow_data **prev; | ||
150 | 138 | ||
151 | pr_debug("atm_tc_put(sch %p,[qdisc %p],flow %p)\n", sch, p, flow); | 139 | pr_debug("atm_tc_put(sch %p,[qdisc %p],flow %p)\n", sch, p, flow); |
152 | if (--flow->ref) | 140 | if (--flow->ref) |
153 | return; | 141 | return; |
154 | pr_debug("atm_tc_put: destroying\n"); | 142 | pr_debug("atm_tc_put: destroying\n"); |
155 | for (prev = &p->flows; *prev; prev = &(*prev)->next) | 143 | list_del_init(&flow->list); |
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); | 144 | pr_debug("atm_tc_put: qdisc %p\n", flow->q); |
164 | qdisc_destroy(flow->q); | 145 | qdisc_destroy(flow->q); |
165 | tcf_destroy_chain(&flow->filter_list); | 146 | tcf_destroy_chain(&flow->filter_list); |
@@ -274,7 +255,7 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent, | |||
274 | error = -EINVAL; | 255 | error = -EINVAL; |
275 | goto err_out; | 256 | goto err_out; |
276 | } | 257 | } |
277 | if (find_flow(p, flow)) { | 258 | if (!list_empty(&flow->list)) { |
278 | error = -EEXIST; | 259 | error = -EEXIST; |
279 | goto err_out; | 260 | goto err_out; |
280 | } | 261 | } |
@@ -313,8 +294,7 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent, | |||
313 | flow->classid = classid; | 294 | flow->classid = classid; |
314 | flow->ref = 1; | 295 | flow->ref = 1; |
315 | flow->excess = excess; | 296 | flow->excess = excess; |
316 | flow->next = p->link.next; | 297 | list_add(&flow->list, &p->link.list); |
317 | p->link.next = flow; | ||
318 | flow->hdr_len = hdr_len; | 298 | flow->hdr_len = hdr_len; |
319 | if (hdr) | 299 | if (hdr) |
320 | memcpy(flow->hdr, hdr, hdr_len); | 300 | memcpy(flow->hdr, hdr, hdr_len); |
@@ -335,7 +315,7 @@ static int atm_tc_delete(struct Qdisc *sch, unsigned long arg) | |||
335 | struct atm_flow_data *flow = (struct atm_flow_data *)arg; | 315 | struct atm_flow_data *flow = (struct atm_flow_data *)arg; |
336 | 316 | ||
337 | pr_debug("atm_tc_delete(sch %p,[qdisc %p],flow %p)\n", sch, p, flow); | 317 | pr_debug("atm_tc_delete(sch %p,[qdisc %p],flow %p)\n", sch, p, flow); |
338 | if (!find_flow(qdisc_priv(sch), flow)) | 318 | if (list_empty(&flow->list)) |
339 | return -EINVAL; | 319 | return -EINVAL; |
340 | if (flow->filter_list || flow == &p->link) | 320 | if (flow->filter_list || flow == &p->link) |
341 | return -EBUSY; | 321 | return -EBUSY; |
@@ -361,12 +341,12 @@ static void atm_tc_walk(struct Qdisc *sch, struct qdisc_walker *walker) | |||
361 | pr_debug("atm_tc_walk(sch %p,[qdisc %p],walker %p)\n", sch, p, walker); | 341 | pr_debug("atm_tc_walk(sch %p,[qdisc %p],walker %p)\n", sch, p, walker); |
362 | if (walker->stop) | 342 | if (walker->stop) |
363 | return; | 343 | return; |
364 | for (flow = p->flows; flow; flow = flow->next) { | 344 | list_for_each_entry(flow, &p->flows, list) { |
365 | if (walker->count >= walker->skip) | 345 | if (walker->count >= walker->skip && |
366 | if (walker->fn(sch, (unsigned long)flow, walker) < 0) { | 346 | walker->fn(sch, (unsigned long)flow, walker) < 0) { |
367 | walker->stop = 1; | 347 | walker->stop = 1; |
368 | break; | 348 | break; |
369 | } | 349 | } |
370 | walker->count++; | 350 | walker->count++; |
371 | } | 351 | } |
372 | } | 352 | } |
@@ -385,16 +365,17 @@ static struct tcf_proto **atm_tc_find_tcf(struct Qdisc *sch, unsigned long cl) | |||
385 | static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch) | 365 | static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch) |
386 | { | 366 | { |
387 | struct atm_qdisc_data *p = qdisc_priv(sch); | 367 | struct atm_qdisc_data *p = qdisc_priv(sch); |
388 | struct atm_flow_data *flow = NULL; /* @@@ */ | 368 | struct atm_flow_data *flow; |
389 | struct tcf_result res; | 369 | struct tcf_result res; |
390 | int result; | 370 | int result; |
391 | int ret = NET_XMIT_POLICED; | 371 | int ret = NET_XMIT_POLICED; |
392 | 372 | ||
393 | pr_debug("atm_tc_enqueue(skb %p,sch %p,[qdisc %p])\n", skb, sch, p); | 373 | pr_debug("atm_tc_enqueue(skb %p,sch %p,[qdisc %p])\n", skb, sch, p); |
394 | result = TC_POLICE_OK; /* be nice to gcc */ | 374 | result = TC_POLICE_OK; /* be nice to gcc */ |
375 | flow = NULL; | ||
395 | if (TC_H_MAJ(skb->priority) != sch->handle || | 376 | if (TC_H_MAJ(skb->priority) != sch->handle || |
396 | !(flow = (struct atm_flow_data *)atm_tc_get(sch, skb->priority))) | 377 | !(flow = (struct atm_flow_data *)atm_tc_get(sch, skb->priority))) { |
397 | for (flow = p->flows; flow; flow = flow->next) | 378 | list_for_each_entry(flow, &p->flows, list) { |
398 | if (flow->filter_list) { | 379 | if (flow->filter_list) { |
399 | result = tc_classify_compat(skb, | 380 | result = tc_classify_compat(skb, |
400 | flow->filter_list, | 381 | flow->filter_list, |
@@ -404,8 +385,13 @@ static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch) | |||
404 | flow = (struct atm_flow_data *)res.class; | 385 | flow = (struct atm_flow_data *)res.class; |
405 | if (!flow) | 386 | if (!flow) |
406 | flow = lookup_flow(sch, res.classid); | 387 | flow = lookup_flow(sch, res.classid); |
407 | break; | 388 | goto done; |
408 | } | 389 | } |
390 | } | ||
391 | flow = NULL; | ||
392 | done: | ||
393 | ; | ||
394 | } | ||
409 | if (!flow) | 395 | if (!flow) |
410 | flow = &p->link; | 396 | flow = &p->link; |
411 | else { | 397 | else { |
@@ -477,7 +463,9 @@ static void sch_atm_dequeue(unsigned long data) | |||
477 | struct sk_buff *skb; | 463 | struct sk_buff *skb; |
478 | 464 | ||
479 | pr_debug("sch_atm_dequeue(sch %p,[qdisc %p])\n", sch, p); | 465 | pr_debug("sch_atm_dequeue(sch %p,[qdisc %p])\n", sch, p); |
480 | for (flow = p->link.next; flow; flow = flow->next) | 466 | list_for_each_entry(flow, &p->flows, list) { |
467 | if (flow == &p->link) | ||
468 | continue; | ||
481 | /* | 469 | /* |
482 | * If traffic is properly shaped, this won't generate nasty | 470 | * If traffic is properly shaped, this won't generate nasty |
483 | * little bursts. Otherwise, it may ... (but that's okay) | 471 | * little bursts. Otherwise, it may ... (but that's okay) |
@@ -512,6 +500,7 @@ static void sch_atm_dequeue(unsigned long data) | |||
512 | /* atm.atm_options are already set by atm_tc_enqueue */ | 500 | /* atm.atm_options are already set by atm_tc_enqueue */ |
513 | flow->vcc->send(flow->vcc, skb); | 501 | flow->vcc->send(flow->vcc, skb); |
514 | } | 502 | } |
503 | } | ||
515 | } | 504 | } |
516 | 505 | ||
517 | static struct sk_buff *atm_tc_dequeue(struct Qdisc *sch) | 506 | static struct sk_buff *atm_tc_dequeue(struct Qdisc *sch) |
@@ -543,9 +532,10 @@ static unsigned int atm_tc_drop(struct Qdisc *sch) | |||
543 | unsigned int len; | 532 | unsigned int len; |
544 | 533 | ||
545 | pr_debug("atm_tc_drop(sch %p,[qdisc %p])\n", sch, p); | 534 | pr_debug("atm_tc_drop(sch %p,[qdisc %p])\n", sch, p); |
546 | for (flow = p->flows; flow; flow = flow->next) | 535 | list_for_each_entry(flow, &p->flows, list) { |
547 | if (flow->q->ops->drop && (len = flow->q->ops->drop(flow->q))) | 536 | if (flow->q->ops->drop && (len = flow->q->ops->drop(flow->q))) |
548 | return len; | 537 | return len; |
538 | } | ||
549 | return 0; | 539 | return 0; |
550 | } | 540 | } |
551 | 541 | ||
@@ -554,7 +544,9 @@ static int atm_tc_init(struct Qdisc *sch, struct nlattr *opt) | |||
554 | struct atm_qdisc_data *p = qdisc_priv(sch); | 544 | struct atm_qdisc_data *p = qdisc_priv(sch); |
555 | 545 | ||
556 | pr_debug("atm_tc_init(sch %p,[qdisc %p],opt %p)\n", sch, p, opt); | 546 | pr_debug("atm_tc_init(sch %p,[qdisc %p],opt %p)\n", sch, p, opt); |
557 | p->flows = &p->link; | 547 | INIT_LIST_HEAD(&p->flows); |
548 | INIT_LIST_HEAD(&p->link.list); | ||
549 | list_add(&p->link.list, &p->flows); | ||
558 | p->link.q = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue, | 550 | p->link.q = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue, |
559 | &pfifo_qdisc_ops, sch->handle); | 551 | &pfifo_qdisc_ops, sch->handle); |
560 | if (!p->link.q) | 552 | if (!p->link.q) |
@@ -565,7 +557,6 @@ static int atm_tc_init(struct Qdisc *sch, struct nlattr *opt) | |||
565 | p->link.sock = NULL; | 557 | p->link.sock = NULL; |
566 | p->link.classid = sch->handle; | 558 | p->link.classid = sch->handle; |
567 | p->link.ref = 1; | 559 | p->link.ref = 1; |
568 | p->link.next = NULL; | ||
569 | tasklet_init(&p->task, sch_atm_dequeue, (unsigned long)sch); | 560 | tasklet_init(&p->task, sch_atm_dequeue, (unsigned long)sch); |
570 | return 0; | 561 | return 0; |
571 | } | 562 | } |
@@ -576,7 +567,7 @@ static void atm_tc_reset(struct Qdisc *sch) | |||
576 | struct atm_flow_data *flow; | 567 | struct atm_flow_data *flow; |
577 | 568 | ||
578 | pr_debug("atm_tc_reset(sch %p,[qdisc %p])\n", sch, p); | 569 | pr_debug("atm_tc_reset(sch %p,[qdisc %p])\n", sch, p); |
579 | for (flow = p->flows; flow; flow = flow->next) | 570 | list_for_each_entry(flow, &p->flows, list) |
580 | qdisc_reset(flow->q); | 571 | qdisc_reset(flow->q); |
581 | sch->q.qlen = 0; | 572 | sch->q.qlen = 0; |
582 | } | 573 | } |
@@ -584,24 +575,17 @@ static void atm_tc_reset(struct Qdisc *sch) | |||
584 | static void atm_tc_destroy(struct Qdisc *sch) | 575 | static void atm_tc_destroy(struct Qdisc *sch) |
585 | { | 576 | { |
586 | struct atm_qdisc_data *p = qdisc_priv(sch); | 577 | struct atm_qdisc_data *p = qdisc_priv(sch); |
587 | struct atm_flow_data *flow; | 578 | struct atm_flow_data *flow, *tmp; |
588 | 579 | ||
589 | pr_debug("atm_tc_destroy(sch %p,[qdisc %p])\n", sch, p); | 580 | pr_debug("atm_tc_destroy(sch %p,[qdisc %p])\n", sch, p); |
590 | for (flow = p->flows; flow; flow = flow->next) | 581 | list_for_each_entry(flow, &p->flows, list) |
591 | tcf_destroy_chain(&flow->filter_list); | 582 | tcf_destroy_chain(&flow->filter_list); |
592 | 583 | ||
593 | /* races ? */ | 584 | list_for_each_entry_safe(flow, tmp, &p->flows, list) { |
594 | while ((flow = p->flows)) { | ||
595 | if (flow->ref > 1) | 585 | if (flow->ref > 1) |
596 | printk(KERN_ERR "atm_destroy: %p->ref = %d\n", flow, | 586 | printk(KERN_ERR "atm_destroy: %p->ref = %d\n", flow, |
597 | flow->ref); | 587 | flow->ref); |
598 | atm_tc_put(sch, (unsigned long)flow); | 588 | 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 | } | 589 | } |
606 | tasklet_kill(&p->task); | 590 | tasklet_kill(&p->task); |
607 | } | 591 | } |
@@ -615,7 +599,7 @@ static int atm_tc_dump_class(struct Qdisc *sch, unsigned long cl, | |||
615 | 599 | ||
616 | pr_debug("atm_tc_dump_class(sch %p,[qdisc %p],flow %p,skb %p,tcm %p)\n", | 600 | pr_debug("atm_tc_dump_class(sch %p,[qdisc %p],flow %p,skb %p,tcm %p)\n", |
617 | sch, p, flow, skb, tcm); | 601 | sch, p, flow, skb, tcm); |
618 | if (!find_flow(p, flow)) | 602 | if (list_empty(&flow->list)) |
619 | return -EINVAL; | 603 | return -EINVAL; |
620 | tcm->tcm_handle = flow->classid; | 604 | tcm->tcm_handle = flow->classid; |
621 | tcm->tcm_info = flow->q->handle; | 605 | tcm->tcm_info = flow->q->handle; |