diff options
author | Florian Westphal <fw@strlen.de> | 2016-09-17 18:57:34 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-09-19 01:47:18 -0400 |
commit | 48da34b7a74201f15315cb1fc40bb9a7bd2b4940 (patch) | |
tree | 123595224534ee49f1175b6ef8371728b6d31538 | |
parent | ed760cb8aae7c2b84c193d4a7637b0c9e752f07e (diff) |
sched: add and use qdisc_skb_head helpers
This change replaces sk_buff_head struct in Qdiscs with new qdisc_skb_head.
Its similar to the skb_buff_head api, but does not use skb->prev pointers.
Qdiscs will commonly enqueue at the tail of a list and dequeue at head.
While skb_buff_head works fine for this, enqueue/dequeue needs to also
adjust the prev pointer of next element.
The ->prev pointer is not required for qdiscs so we can just leave
it undefined and avoid one cacheline write access for en/dequeue.
Suggested-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/sch_generic.h | 63 | ||||
-rw-r--r-- | net/sched/sch_generic.c | 21 | ||||
-rw-r--r-- | net/sched/sch_htb.c | 24 | ||||
-rw-r--r-- | net/sched/sch_netem.c | 14 |
4 files changed, 94 insertions, 28 deletions
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 0741ed41575b..e6aa0a249672 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h | |||
@@ -36,6 +36,14 @@ struct qdisc_size_table { | |||
36 | u16 data[]; | 36 | u16 data[]; |
37 | }; | 37 | }; |
38 | 38 | ||
39 | /* similar to sk_buff_head, but skb->prev pointer is undefined. */ | ||
40 | struct qdisc_skb_head { | ||
41 | struct sk_buff *head; | ||
42 | struct sk_buff *tail; | ||
43 | __u32 qlen; | ||
44 | spinlock_t lock; | ||
45 | }; | ||
46 | |||
39 | struct Qdisc { | 47 | struct Qdisc { |
40 | int (*enqueue)(struct sk_buff *skb, | 48 | int (*enqueue)(struct sk_buff *skb, |
41 | struct Qdisc *sch, | 49 | struct Qdisc *sch, |
@@ -76,7 +84,7 @@ struct Qdisc { | |||
76 | * For performance sake on SMP, we put highly modified fields at the end | 84 | * For performance sake on SMP, we put highly modified fields at the end |
77 | */ | 85 | */ |
78 | struct sk_buff *gso_skb ____cacheline_aligned_in_smp; | 86 | struct sk_buff *gso_skb ____cacheline_aligned_in_smp; |
79 | struct sk_buff_head q; | 87 | struct qdisc_skb_head q; |
80 | struct gnet_stats_basic_packed bstats; | 88 | struct gnet_stats_basic_packed bstats; |
81 | seqcount_t running; | 89 | seqcount_t running; |
82 | struct gnet_stats_queue qstats; | 90 | struct gnet_stats_queue qstats; |
@@ -600,10 +608,27 @@ static inline void qdisc_qstats_overlimit(struct Qdisc *sch) | |||
600 | sch->qstats.overlimits++; | 608 | sch->qstats.overlimits++; |
601 | } | 609 | } |
602 | 610 | ||
611 | static inline void qdisc_skb_head_init(struct qdisc_skb_head *qh) | ||
612 | { | ||
613 | qh->head = NULL; | ||
614 | qh->tail = NULL; | ||
615 | qh->qlen = 0; | ||
616 | } | ||
617 | |||
603 | static inline int __qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch, | 618 | static inline int __qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch, |
604 | struct sk_buff_head *list) | 619 | struct qdisc_skb_head *qh) |
605 | { | 620 | { |
606 | __skb_queue_tail(list, skb); | 621 | struct sk_buff *last = qh->tail; |
622 | |||
623 | if (last) { | ||
624 | skb->next = NULL; | ||
625 | last->next = skb; | ||
626 | qh->tail = skb; | ||
627 | } else { | ||
628 | qh->tail = skb; | ||
629 | qh->head = skb; | ||
630 | } | ||
631 | qh->qlen++; | ||
607 | qdisc_qstats_backlog_inc(sch, skb); | 632 | qdisc_qstats_backlog_inc(sch, skb); |
608 | 633 | ||
609 | return NET_XMIT_SUCCESS; | 634 | return NET_XMIT_SUCCESS; |
@@ -614,9 +639,17 @@ static inline int qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch) | |||
614 | return __qdisc_enqueue_tail(skb, sch, &sch->q); | 639 | return __qdisc_enqueue_tail(skb, sch, &sch->q); |
615 | } | 640 | } |
616 | 641 | ||
617 | static inline struct sk_buff *__qdisc_dequeue_head(struct sk_buff_head *list) | 642 | static inline struct sk_buff *__qdisc_dequeue_head(struct qdisc_skb_head *qh) |
618 | { | 643 | { |
619 | struct sk_buff *skb = __skb_dequeue(list); | 644 | struct sk_buff *skb = qh->head; |
645 | |||
646 | if (likely(skb != NULL)) { | ||
647 | qh->head = skb->next; | ||
648 | qh->qlen--; | ||
649 | if (qh->head == NULL) | ||
650 | qh->tail = NULL; | ||
651 | skb->next = NULL; | ||
652 | } | ||
620 | 653 | ||
621 | return skb; | 654 | return skb; |
622 | } | 655 | } |
@@ -643,10 +676,10 @@ static inline void __qdisc_drop(struct sk_buff *skb, struct sk_buff **to_free) | |||
643 | } | 676 | } |
644 | 677 | ||
645 | static inline unsigned int __qdisc_queue_drop_head(struct Qdisc *sch, | 678 | static inline unsigned int __qdisc_queue_drop_head(struct Qdisc *sch, |
646 | struct sk_buff_head *list, | 679 | struct qdisc_skb_head *qh, |
647 | struct sk_buff **to_free) | 680 | struct sk_buff **to_free) |
648 | { | 681 | { |
649 | struct sk_buff *skb = __skb_dequeue(list); | 682 | struct sk_buff *skb = __qdisc_dequeue_head(qh); |
650 | 683 | ||
651 | if (likely(skb != NULL)) { | 684 | if (likely(skb != NULL)) { |
652 | unsigned int len = qdisc_pkt_len(skb); | 685 | unsigned int len = qdisc_pkt_len(skb); |
@@ -667,7 +700,9 @@ static inline unsigned int qdisc_queue_drop_head(struct Qdisc *sch, | |||
667 | 700 | ||
668 | static inline struct sk_buff *qdisc_peek_head(struct Qdisc *sch) | 701 | static inline struct sk_buff *qdisc_peek_head(struct Qdisc *sch) |
669 | { | 702 | { |
670 | return skb_peek(&sch->q); | 703 | const struct qdisc_skb_head *qh = &sch->q; |
704 | |||
705 | return qh->head; | ||
671 | } | 706 | } |
672 | 707 | ||
673 | /* generic pseudo peek method for non-work-conserving qdisc */ | 708 | /* generic pseudo peek method for non-work-conserving qdisc */ |
@@ -702,15 +737,19 @@ static inline struct sk_buff *qdisc_dequeue_peeked(struct Qdisc *sch) | |||
702 | return skb; | 737 | return skb; |
703 | } | 738 | } |
704 | 739 | ||
705 | static inline void __qdisc_reset_queue(struct sk_buff_head *list) | 740 | static inline void __qdisc_reset_queue(struct qdisc_skb_head *qh) |
706 | { | 741 | { |
707 | /* | 742 | /* |
708 | * We do not know the backlog in bytes of this list, it | 743 | * We do not know the backlog in bytes of this list, it |
709 | * is up to the caller to correct it | 744 | * is up to the caller to correct it |
710 | */ | 745 | */ |
711 | if (!skb_queue_empty(list)) { | 746 | ASSERT_RTNL(); |
712 | rtnl_kfree_skbs(list->next, list->prev); | 747 | if (qh->qlen) { |
713 | __skb_queue_head_init(list); | 748 | rtnl_kfree_skbs(qh->head, qh->tail); |
749 | |||
750 | qh->head = NULL; | ||
751 | qh->tail = NULL; | ||
752 | qh->qlen = 0; | ||
714 | } | 753 | } |
715 | } | 754 | } |
716 | 755 | ||
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 73877d9c2bcb..6cfb6e9038c2 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c | |||
@@ -466,7 +466,7 @@ static const u8 prio2band[TC_PRIO_MAX + 1] = { | |||
466 | */ | 466 | */ |
467 | struct pfifo_fast_priv { | 467 | struct pfifo_fast_priv { |
468 | u32 bitmap; | 468 | u32 bitmap; |
469 | struct sk_buff_head q[PFIFO_FAST_BANDS]; | 469 | struct qdisc_skb_head q[PFIFO_FAST_BANDS]; |
470 | }; | 470 | }; |
471 | 471 | ||
472 | /* | 472 | /* |
@@ -477,7 +477,7 @@ struct pfifo_fast_priv { | |||
477 | */ | 477 | */ |
478 | static const int bitmap2band[] = {-1, 0, 1, 0, 2, 0, 1, 0}; | 478 | static const int bitmap2band[] = {-1, 0, 1, 0, 2, 0, 1, 0}; |
479 | 479 | ||
480 | static inline struct sk_buff_head *band2list(struct pfifo_fast_priv *priv, | 480 | static inline struct qdisc_skb_head *band2list(struct pfifo_fast_priv *priv, |
481 | int band) | 481 | int band) |
482 | { | 482 | { |
483 | return priv->q + band; | 483 | return priv->q + band; |
@@ -489,7 +489,7 @@ static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc *qdisc, | |||
489 | if (qdisc->q.qlen < qdisc_dev(qdisc)->tx_queue_len) { | 489 | if (qdisc->q.qlen < qdisc_dev(qdisc)->tx_queue_len) { |
490 | int band = prio2band[skb->priority & TC_PRIO_MAX]; | 490 | int band = prio2band[skb->priority & TC_PRIO_MAX]; |
491 | struct pfifo_fast_priv *priv = qdisc_priv(qdisc); | 491 | struct pfifo_fast_priv *priv = qdisc_priv(qdisc); |
492 | struct sk_buff_head *list = band2list(priv, band); | 492 | struct qdisc_skb_head *list = band2list(priv, band); |
493 | 493 | ||
494 | priv->bitmap |= (1 << band); | 494 | priv->bitmap |= (1 << band); |
495 | qdisc->q.qlen++; | 495 | qdisc->q.qlen++; |
@@ -505,8 +505,8 @@ static struct sk_buff *pfifo_fast_dequeue(struct Qdisc *qdisc) | |||
505 | int band = bitmap2band[priv->bitmap]; | 505 | int band = bitmap2band[priv->bitmap]; |
506 | 506 | ||
507 | if (likely(band >= 0)) { | 507 | if (likely(band >= 0)) { |
508 | struct sk_buff_head *list = band2list(priv, band); | 508 | struct qdisc_skb_head *qh = band2list(priv, band); |
509 | struct sk_buff *skb = __qdisc_dequeue_head(list); | 509 | struct sk_buff *skb = __qdisc_dequeue_head(qh); |
510 | 510 | ||
511 | if (likely(skb != NULL)) { | 511 | if (likely(skb != NULL)) { |
512 | qdisc_qstats_backlog_dec(qdisc, skb); | 512 | qdisc_qstats_backlog_dec(qdisc, skb); |
@@ -514,7 +514,7 @@ static struct sk_buff *pfifo_fast_dequeue(struct Qdisc *qdisc) | |||
514 | } | 514 | } |
515 | 515 | ||
516 | qdisc->q.qlen--; | 516 | qdisc->q.qlen--; |
517 | if (skb_queue_empty(list)) | 517 | if (qh->qlen == 0) |
518 | priv->bitmap &= ~(1 << band); | 518 | priv->bitmap &= ~(1 << band); |
519 | 519 | ||
520 | return skb; | 520 | return skb; |
@@ -529,9 +529,9 @@ static struct sk_buff *pfifo_fast_peek(struct Qdisc *qdisc) | |||
529 | int band = bitmap2band[priv->bitmap]; | 529 | int band = bitmap2band[priv->bitmap]; |
530 | 530 | ||
531 | if (band >= 0) { | 531 | if (band >= 0) { |
532 | struct sk_buff_head *list = band2list(priv, band); | 532 | struct qdisc_skb_head *qh = band2list(priv, band); |
533 | 533 | ||
534 | return skb_peek(list); | 534 | return qh->head; |
535 | } | 535 | } |
536 | 536 | ||
537 | return NULL; | 537 | return NULL; |
@@ -569,7 +569,7 @@ static int pfifo_fast_init(struct Qdisc *qdisc, struct nlattr *opt) | |||
569 | struct pfifo_fast_priv *priv = qdisc_priv(qdisc); | 569 | struct pfifo_fast_priv *priv = qdisc_priv(qdisc); |
570 | 570 | ||
571 | for (prio = 0; prio < PFIFO_FAST_BANDS; prio++) | 571 | for (prio = 0; prio < PFIFO_FAST_BANDS; prio++) |
572 | __skb_queue_head_init(band2list(priv, prio)); | 572 | qdisc_skb_head_init(band2list(priv, prio)); |
573 | 573 | ||
574 | /* Can by-pass the queue discipline */ | 574 | /* Can by-pass the queue discipline */ |
575 | qdisc->flags |= TCQ_F_CAN_BYPASS; | 575 | qdisc->flags |= TCQ_F_CAN_BYPASS; |
@@ -617,7 +617,8 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, | |||
617 | sch = (struct Qdisc *) QDISC_ALIGN((unsigned long) p); | 617 | sch = (struct Qdisc *) QDISC_ALIGN((unsigned long) p); |
618 | sch->padded = (char *) sch - (char *) p; | 618 | sch->padded = (char *) sch - (char *) p; |
619 | } | 619 | } |
620 | skb_queue_head_init(&sch->q); | 620 | qdisc_skb_head_init(&sch->q); |
621 | spin_lock_init(&sch->q.lock); | ||
621 | 622 | ||
622 | spin_lock_init(&sch->busylock); | 623 | spin_lock_init(&sch->busylock); |
623 | lockdep_set_class(&sch->busylock, | 624 | lockdep_set_class(&sch->busylock, |
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 53dbfa187870..c798d0de8a9d 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c | |||
@@ -162,7 +162,7 @@ struct htb_sched { | |||
162 | struct work_struct work; | 162 | struct work_struct work; |
163 | 163 | ||
164 | /* non shaped skbs; let them go directly thru */ | 164 | /* non shaped skbs; let them go directly thru */ |
165 | struct sk_buff_head direct_queue; | 165 | struct qdisc_skb_head direct_queue; |
166 | long direct_pkts; | 166 | long direct_pkts; |
167 | 167 | ||
168 | struct qdisc_watchdog watchdog; | 168 | struct qdisc_watchdog watchdog; |
@@ -570,6 +570,22 @@ static inline void htb_deactivate(struct htb_sched *q, struct htb_class *cl) | |||
570 | list_del_init(&cl->un.leaf.drop_list); | 570 | list_del_init(&cl->un.leaf.drop_list); |
571 | } | 571 | } |
572 | 572 | ||
573 | static void htb_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch, | ||
574 | struct qdisc_skb_head *qh) | ||
575 | { | ||
576 | struct sk_buff *last = qh->tail; | ||
577 | |||
578 | if (last) { | ||
579 | skb->next = NULL; | ||
580 | last->next = skb; | ||
581 | qh->tail = skb; | ||
582 | } else { | ||
583 | qh->tail = skb; | ||
584 | qh->head = skb; | ||
585 | } | ||
586 | qh->qlen++; | ||
587 | } | ||
588 | |||
573 | static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch, | 589 | static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch, |
574 | struct sk_buff **to_free) | 590 | struct sk_buff **to_free) |
575 | { | 591 | { |
@@ -580,7 +596,7 @@ static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch, | |||
580 | if (cl == HTB_DIRECT) { | 596 | if (cl == HTB_DIRECT) { |
581 | /* enqueue to helper queue */ | 597 | /* enqueue to helper queue */ |
582 | if (q->direct_queue.qlen < q->direct_qlen) { | 598 | if (q->direct_queue.qlen < q->direct_qlen) { |
583 | __skb_queue_tail(&q->direct_queue, skb); | 599 | htb_enqueue_tail(skb, sch, &q->direct_queue); |
584 | q->direct_pkts++; | 600 | q->direct_pkts++; |
585 | } else { | 601 | } else { |
586 | return qdisc_drop(skb, sch, to_free); | 602 | return qdisc_drop(skb, sch, to_free); |
@@ -888,7 +904,7 @@ static struct sk_buff *htb_dequeue(struct Qdisc *sch) | |||
888 | unsigned long start_at; | 904 | unsigned long start_at; |
889 | 905 | ||
890 | /* try to dequeue direct packets as high prio (!) to minimize cpu work */ | 906 | /* try to dequeue direct packets as high prio (!) to minimize cpu work */ |
891 | skb = __skb_dequeue(&q->direct_queue); | 907 | skb = __qdisc_dequeue_head(&q->direct_queue); |
892 | if (skb != NULL) { | 908 | if (skb != NULL) { |
893 | ok: | 909 | ok: |
894 | qdisc_bstats_update(sch, skb); | 910 | qdisc_bstats_update(sch, skb); |
@@ -1019,7 +1035,7 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt) | |||
1019 | 1035 | ||
1020 | qdisc_watchdog_init(&q->watchdog, sch); | 1036 | qdisc_watchdog_init(&q->watchdog, sch); |
1021 | INIT_WORK(&q->work, htb_work_func); | 1037 | INIT_WORK(&q->work, htb_work_func); |
1022 | __skb_queue_head_init(&q->direct_queue); | 1038 | qdisc_skb_head_init(&q->direct_queue); |
1023 | 1039 | ||
1024 | if (tb[TCA_HTB_DIRECT_QLEN]) | 1040 | if (tb[TCA_HTB_DIRECT_QLEN]) |
1025 | q->direct_qlen = nla_get_u32(tb[TCA_HTB_DIRECT_QLEN]); | 1041 | q->direct_qlen = nla_get_u32(tb[TCA_HTB_DIRECT_QLEN]); |
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 0a964b35f8c7..9f7b380cf0a3 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c | |||
@@ -413,6 +413,16 @@ static struct sk_buff *netem_segment(struct sk_buff *skb, struct Qdisc *sch, | |||
413 | return segs; | 413 | return segs; |
414 | } | 414 | } |
415 | 415 | ||
416 | static void netem_enqueue_skb_head(struct qdisc_skb_head *qh, struct sk_buff *skb) | ||
417 | { | ||
418 | skb->next = qh->head; | ||
419 | |||
420 | if (!qh->head) | ||
421 | qh->tail = skb; | ||
422 | qh->head = skb; | ||
423 | qh->qlen++; | ||
424 | } | ||
425 | |||
416 | /* | 426 | /* |
417 | * Insert one skb into qdisc. | 427 | * Insert one skb into qdisc. |
418 | * Note: parent depends on return value to account for queue length. | 428 | * Note: parent depends on return value to account for queue length. |
@@ -523,7 +533,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, | |||
523 | struct sk_buff *last; | 533 | struct sk_buff *last; |
524 | 534 | ||
525 | if (sch->q.qlen) | 535 | if (sch->q.qlen) |
526 | last = skb_peek_tail(&sch->q); | 536 | last = sch->q.tail; |
527 | else | 537 | else |
528 | last = netem_rb_to_skb(rb_last(&q->t_root)); | 538 | last = netem_rb_to_skb(rb_last(&q->t_root)); |
529 | if (last) { | 539 | if (last) { |
@@ -552,7 +562,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, | |||
552 | cb->time_to_send = psched_get_time(); | 562 | cb->time_to_send = psched_get_time(); |
553 | q->counter = 0; | 563 | q->counter = 0; |
554 | 564 | ||
555 | __skb_queue_head(&sch->q, skb); | 565 | netem_enqueue_skb_head(&sch->q, skb); |
556 | sch->qstats.requeues++; | 566 | sch->qstats.requeues++; |
557 | } | 567 | } |
558 | 568 | ||