Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Florian Westphal and committed by
David S. Miller
48da34b7 ed760cb8

+95 -29
+52 -13
include/net/sch_generic.h
··· 36 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 47 struct Qdisc { 40 48 int (*enqueue)(struct sk_buff *skb, 41 49 struct Qdisc *sch, ··· 84 76 * For performance sake on SMP, we put highly modified fields at the end 85 77 */ 86 78 struct sk_buff *gso_skb ____cacheline_aligned_in_smp; 87 - struct sk_buff_head q; 79 + struct qdisc_skb_head q; 88 80 struct gnet_stats_basic_packed bstats; 89 81 seqcount_t running; 90 82 struct gnet_stats_queue qstats; ··· 608 600 sch->qstats.overlimits++; 609 601 } 610 602 611 - static inline int __qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch, 612 - struct sk_buff_head *list) 603 + static inline void qdisc_skb_head_init(struct qdisc_skb_head *qh) 613 604 { 614 - __skb_queue_tail(list, skb); 605 + qh->head = NULL; 606 + qh->tail = NULL; 607 + qh->qlen = 0; 608 + } 609 + 610 + static inline int __qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch, 611 + struct qdisc_skb_head *qh) 612 + { 613 + struct sk_buff *last = qh->tail; 614 + 615 + if (last) { 616 + skb->next = NULL; 617 + last->next = skb; 618 + qh->tail = skb; 619 + } else { 620 + qh->tail = skb; 621 + qh->head = skb; 622 + } 623 + qh->qlen++; 615 624 qdisc_qstats_backlog_inc(sch, skb); 616 625 617 626 return NET_XMIT_SUCCESS; ··· 639 614 return __qdisc_enqueue_tail(skb, sch, &sch->q); 640 615 } 641 616 642 - static inline struct sk_buff *__qdisc_dequeue_head(struct sk_buff_head *list) 617 + static inline struct sk_buff *__qdisc_dequeue_head(struct qdisc_skb_head *qh) 643 618 { 644 - struct sk_buff *skb = __skb_dequeue(list); 619 + struct sk_buff *skb = qh->head; 620 + 621 + if (likely(skb != NULL)) { 622 + qh->head = skb->next; 623 + qh->qlen--; 624 + if (qh->head == NULL) 625 + qh->tail = NULL; 626 + skb->next = NULL; 627 + } 645 628 646 629 return skb; 647 630 } ··· 676 643 } 677 644 678 645 static inline unsigned int __qdisc_queue_drop_head(struct Qdisc *sch, 679 - struct sk_buff_head *list, 646 + struct qdisc_skb_head *qh, 680 647 struct sk_buff **to_free) 681 648 { 682 - struct sk_buff *skb = __skb_dequeue(list); 649 + struct sk_buff *skb = __qdisc_dequeue_head(qh); 683 650 684 651 if (likely(skb != NULL)) { 685 652 unsigned int len = qdisc_pkt_len(skb); ··· 700 667 701 668 static inline struct sk_buff *qdisc_peek_head(struct Qdisc *sch) 702 669 { 703 - return skb_peek(&sch->q); 670 + const struct qdisc_skb_head *qh = &sch->q; 671 + 672 + return qh->head; 704 673 } 705 674 706 675 /* generic pseudo peek method for non-work-conserving qdisc */ ··· 737 702 return skb; 738 703 } 739 704 740 - static inline void __qdisc_reset_queue(struct sk_buff_head *list) 705 + static inline void __qdisc_reset_queue(struct qdisc_skb_head *qh) 741 706 { 742 707 /* 743 708 * We do not know the backlog in bytes of this list, it 744 709 * is up to the caller to correct it 745 710 */ 746 - if (!skb_queue_empty(list)) { 747 - rtnl_kfree_skbs(list->next, list->prev); 748 - __skb_queue_head_init(list); 711 + ASSERT_RTNL(); 712 + if (qh->qlen) { 713 + rtnl_kfree_skbs(qh->head, qh->tail); 714 + 715 + qh->head = NULL; 716 + qh->tail = NULL; 717 + qh->qlen = 0; 749 718 } 750 719 } 751 720
+11 -10
net/sched/sch_generic.c
··· 466 466 */ 467 467 struct pfifo_fast_priv { 468 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 477 */ 478 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 481 int band) 482 482 { 483 483 return priv->q + band; ··· 489 489 if (qdisc->q.qlen < qdisc_dev(qdisc)->tx_queue_len) { 490 490 int band = prio2band[skb->priority & TC_PRIO_MAX]; 491 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 494 priv->bitmap |= (1 << band); 495 495 qdisc->q.qlen++; ··· 505 505 int band = bitmap2band[priv->bitmap]; 506 506 507 507 if (likely(band >= 0)) { 508 - struct sk_buff_head *list = band2list(priv, band); 509 - struct sk_buff *skb = __qdisc_dequeue_head(list); 508 + struct qdisc_skb_head *qh = band2list(priv, band); 509 + struct sk_buff *skb = __qdisc_dequeue_head(qh); 510 510 511 511 if (likely(skb != NULL)) { 512 512 qdisc_qstats_backlog_dec(qdisc, skb); ··· 514 514 } 515 515 516 516 qdisc->q.qlen--; 517 - if (skb_queue_empty(list)) 517 + if (qh->qlen == 0) 518 518 priv->bitmap &= ~(1 << band); 519 519 520 520 return skb; ··· 529 529 int band = bitmap2band[priv->bitmap]; 530 530 531 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 537 return NULL; ··· 569 569 struct pfifo_fast_priv *priv = qdisc_priv(qdisc); 570 570 571 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 574 /* Can by-pass the queue discipline */ 575 575 qdisc->flags |= TCQ_F_CAN_BYPASS; ··· 617 617 sch = (struct Qdisc *) QDISC_ALIGN((unsigned long) p); 618 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 623 spin_lock_init(&sch->busylock); 623 624 lockdep_set_class(&sch->busylock,
+20 -4
net/sched/sch_htb.c
··· 162 162 struct work_struct work; 163 163 164 164 /* non shaped skbs; let them go directly thru */ 165 - struct sk_buff_head direct_queue; 165 + struct qdisc_skb_head direct_queue; 166 166 long direct_pkts; 167 167 168 168 struct qdisc_watchdog watchdog; ··· 570 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 589 static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch, 574 590 struct sk_buff **to_free) 575 591 { ··· 596 580 if (cl == HTB_DIRECT) { 597 581 /* enqueue to helper queue */ 598 582 if (q->direct_queue.qlen < q->direct_qlen) { 599 - __skb_queue_tail(&q->direct_queue, skb); 583 + htb_enqueue_tail(skb, sch, &q->direct_queue); 600 584 q->direct_pkts++; 601 585 } else { 602 586 return qdisc_drop(skb, sch, to_free); ··· 904 888 unsigned long start_at; 905 889 906 890 /* try to dequeue direct packets as high prio (!) to minimize cpu work */ 907 - skb = __skb_dequeue(&q->direct_queue); 891 + skb = __qdisc_dequeue_head(&q->direct_queue); 908 892 if (skb != NULL) { 909 893 ok: 910 894 qdisc_bstats_update(sch, skb); ··· 1035 1019 1036 1020 qdisc_watchdog_init(&q->watchdog, sch); 1037 1021 INIT_WORK(&q->work, htb_work_func); 1038 - __skb_queue_head_init(&q->direct_queue); 1022 + qdisc_skb_head_init(&q->direct_queue); 1039 1023 1040 1024 if (tb[TCA_HTB_DIRECT_QLEN]) 1041 1025 q->direct_qlen = nla_get_u32(tb[TCA_HTB_DIRECT_QLEN]);
+12 -2
net/sched/sch_netem.c
··· 413 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 427 * Insert one skb into qdisc. 418 428 * Note: parent depends on return value to account for queue length. ··· 533 523 struct sk_buff *last; 534 524 535 525 if (sch->q.qlen) 536 - last = skb_peek_tail(&sch->q); 526 + last = sch->q.tail; 537 527 else 538 528 last = netem_rb_to_skb(rb_last(&q->t_root)); 539 529 if (last) { ··· 562 552 cb->time_to_send = psched_get_time(); 563 553 q->counter = 0; 564 554 565 - __skb_queue_head(&sch->q, skb); 555 + netem_enqueue_skb_head(&sch->q, skb); 566 556 sch->qstats.requeues++; 567 557 } 568 558