diff options
Diffstat (limited to 'net/sched/sch_fq.c')
-rw-r--r-- | net/sched/sch_fq.c | 102 |
1 files changed, 63 insertions, 39 deletions
diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c index 32ad015ee8ce..a2fef8b10b96 100644 --- a/net/sched/sch_fq.c +++ b/net/sched/sch_fq.c | |||
@@ -285,7 +285,7 @@ static struct fq_flow *fq_classify(struct sk_buff *skb, struct fq_sched_data *q) | |||
285 | 285 | ||
286 | 286 | ||
287 | /* remove one skb from head of flow queue */ | 287 | /* remove one skb from head of flow queue */ |
288 | static struct sk_buff *fq_dequeue_head(struct fq_flow *flow) | 288 | static struct sk_buff *fq_dequeue_head(struct Qdisc *sch, struct fq_flow *flow) |
289 | { | 289 | { |
290 | struct sk_buff *skb = flow->head; | 290 | struct sk_buff *skb = flow->head; |
291 | 291 | ||
@@ -293,6 +293,8 @@ static struct sk_buff *fq_dequeue_head(struct fq_flow *flow) | |||
293 | flow->head = skb->next; | 293 | flow->head = skb->next; |
294 | skb->next = NULL; | 294 | skb->next = NULL; |
295 | flow->qlen--; | 295 | flow->qlen--; |
296 | sch->qstats.backlog -= qdisc_pkt_len(skb); | ||
297 | sch->q.qlen--; | ||
296 | } | 298 | } |
297 | return skb; | 299 | return skb; |
298 | } | 300 | } |
@@ -418,8 +420,9 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch) | |||
418 | struct fq_flow_head *head; | 420 | struct fq_flow_head *head; |
419 | struct sk_buff *skb; | 421 | struct sk_buff *skb; |
420 | struct fq_flow *f; | 422 | struct fq_flow *f; |
423 | u32 rate; | ||
421 | 424 | ||
422 | skb = fq_dequeue_head(&q->internal); | 425 | skb = fq_dequeue_head(sch, &q->internal); |
423 | if (skb) | 426 | if (skb) |
424 | goto out; | 427 | goto out; |
425 | fq_check_throttled(q, now); | 428 | fq_check_throttled(q, now); |
@@ -449,7 +452,7 @@ begin: | |||
449 | goto begin; | 452 | goto begin; |
450 | } | 453 | } |
451 | 454 | ||
452 | skb = fq_dequeue_head(f); | 455 | skb = fq_dequeue_head(sch, f); |
453 | if (!skb) { | 456 | if (!skb) { |
454 | head->first = f->next; | 457 | head->first = f->next; |
455 | /* force a pass through old_flows to prevent starvation */ | 458 | /* force a pass through old_flows to prevent starvation */ |
@@ -466,43 +469,74 @@ begin: | |||
466 | f->time_next_packet = now; | 469 | f->time_next_packet = now; |
467 | f->credit -= qdisc_pkt_len(skb); | 470 | f->credit -= qdisc_pkt_len(skb); |
468 | 471 | ||
469 | if (f->credit <= 0 && | 472 | if (f->credit > 0 || !q->rate_enable) |
470 | q->rate_enable && | 473 | goto out; |
471 | skb->sk && skb->sk->sk_state != TCP_TIME_WAIT) { | ||
472 | u32 rate = skb->sk->sk_pacing_rate ?: q->flow_default_rate; | ||
473 | 474 | ||
474 | rate = min(rate, q->flow_max_rate); | 475 | if (skb->sk && skb->sk->sk_state != TCP_TIME_WAIT) { |
475 | if (rate) { | 476 | rate = skb->sk->sk_pacing_rate ?: q->flow_default_rate; |
476 | u64 len = (u64)qdisc_pkt_len(skb) * NSEC_PER_SEC; | ||
477 | |||
478 | do_div(len, rate); | ||
479 | /* Since socket rate can change later, | ||
480 | * clamp the delay to 125 ms. | ||
481 | * TODO: maybe segment the too big skb, as in commit | ||
482 | * e43ac79a4bc ("sch_tbf: segment too big GSO packets") | ||
483 | */ | ||
484 | if (unlikely(len > 125 * NSEC_PER_MSEC)) { | ||
485 | len = 125 * NSEC_PER_MSEC; | ||
486 | q->stat_pkts_too_long++; | ||
487 | } | ||
488 | 477 | ||
489 | f->time_next_packet = now + len; | 478 | rate = min(rate, q->flow_max_rate); |
479 | } else { | ||
480 | rate = q->flow_max_rate; | ||
481 | if (rate == ~0U) | ||
482 | goto out; | ||
483 | } | ||
484 | if (rate) { | ||
485 | u32 plen = max(qdisc_pkt_len(skb), q->quantum); | ||
486 | u64 len = (u64)plen * NSEC_PER_SEC; | ||
487 | |||
488 | do_div(len, rate); | ||
489 | /* Since socket rate can change later, | ||
490 | * clamp the delay to 125 ms. | ||
491 | * TODO: maybe segment the too big skb, as in commit | ||
492 | * e43ac79a4bc ("sch_tbf: segment too big GSO packets") | ||
493 | */ | ||
494 | if (unlikely(len > 125 * NSEC_PER_MSEC)) { | ||
495 | len = 125 * NSEC_PER_MSEC; | ||
496 | q->stat_pkts_too_long++; | ||
490 | } | 497 | } |
498 | |||
499 | f->time_next_packet = now + len; | ||
491 | } | 500 | } |
492 | out: | 501 | out: |
493 | sch->qstats.backlog -= qdisc_pkt_len(skb); | ||
494 | qdisc_bstats_update(sch, skb); | 502 | qdisc_bstats_update(sch, skb); |
495 | sch->q.qlen--; | ||
496 | qdisc_unthrottled(sch); | 503 | qdisc_unthrottled(sch); |
497 | return skb; | 504 | return skb; |
498 | } | 505 | } |
499 | 506 | ||
500 | static void fq_reset(struct Qdisc *sch) | 507 | static void fq_reset(struct Qdisc *sch) |
501 | { | 508 | { |
509 | struct fq_sched_data *q = qdisc_priv(sch); | ||
510 | struct rb_root *root; | ||
502 | struct sk_buff *skb; | 511 | struct sk_buff *skb; |
512 | struct rb_node *p; | ||
513 | struct fq_flow *f; | ||
514 | unsigned int idx; | ||
503 | 515 | ||
504 | while ((skb = fq_dequeue(sch)) != NULL) | 516 | while ((skb = fq_dequeue_head(sch, &q->internal)) != NULL) |
505 | kfree_skb(skb); | 517 | kfree_skb(skb); |
518 | |||
519 | if (!q->fq_root) | ||
520 | return; | ||
521 | |||
522 | for (idx = 0; idx < (1U << q->fq_trees_log); idx++) { | ||
523 | root = &q->fq_root[idx]; | ||
524 | while ((p = rb_first(root)) != NULL) { | ||
525 | f = container_of(p, struct fq_flow, fq_node); | ||
526 | rb_erase(p, root); | ||
527 | |||
528 | while ((skb = fq_dequeue_head(sch, f)) != NULL) | ||
529 | kfree_skb(skb); | ||
530 | |||
531 | kmem_cache_free(fq_flow_cachep, f); | ||
532 | } | ||
533 | } | ||
534 | q->new_flows.first = NULL; | ||
535 | q->old_flows.first = NULL; | ||
536 | q->delayed = RB_ROOT; | ||
537 | q->flows = 0; | ||
538 | q->inactive_flows = 0; | ||
539 | q->throttled_flows = 0; | ||
506 | } | 540 | } |
507 | 541 | ||
508 | static void fq_rehash(struct fq_sched_data *q, | 542 | static void fq_rehash(struct fq_sched_data *q, |
@@ -645,6 +679,8 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt) | |||
645 | while (sch->q.qlen > sch->limit) { | 679 | while (sch->q.qlen > sch->limit) { |
646 | struct sk_buff *skb = fq_dequeue(sch); | 680 | struct sk_buff *skb = fq_dequeue(sch); |
647 | 681 | ||
682 | if (!skb) | ||
683 | break; | ||
648 | kfree_skb(skb); | 684 | kfree_skb(skb); |
649 | drop_count++; | 685 | drop_count++; |
650 | } | 686 | } |
@@ -657,21 +693,9 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt) | |||
657 | static void fq_destroy(struct Qdisc *sch) | 693 | static void fq_destroy(struct Qdisc *sch) |
658 | { | 694 | { |
659 | struct fq_sched_data *q = qdisc_priv(sch); | 695 | struct fq_sched_data *q = qdisc_priv(sch); |
660 | struct rb_root *root; | ||
661 | struct rb_node *p; | ||
662 | unsigned int idx; | ||
663 | 696 | ||
664 | if (q->fq_root) { | 697 | fq_reset(sch); |
665 | for (idx = 0; idx < (1U << q->fq_trees_log); idx++) { | 698 | kfree(q->fq_root); |
666 | root = &q->fq_root[idx]; | ||
667 | while ((p = rb_first(root)) != NULL) { | ||
668 | rb_erase(p, root); | ||
669 | kmem_cache_free(fq_flow_cachep, | ||
670 | container_of(p, struct fq_flow, fq_node)); | ||
671 | } | ||
672 | } | ||
673 | kfree(q->fq_root); | ||
674 | } | ||
675 | qdisc_watchdog_cancel(&q->watchdog); | 699 | qdisc_watchdog_cancel(&q->watchdog); |
676 | } | 700 | } |
677 | 701 | ||