aboutsummaryrefslogtreecommitdiffstats
path: root/net/sched/sch_generic.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/sched/sch_generic.c')
-rw-r--r--net/sched/sch_generic.c40
1 files changed, 38 insertions, 2 deletions
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 7c8e5d73d433..797ebef73642 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -56,6 +56,35 @@ static inline int dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q)
return 0;
}
+static struct sk_buff *try_bulk_dequeue_skb(struct Qdisc *q,
+ struct sk_buff *head_skb,
+ int bytelimit)
+{
+ struct sk_buff *skb, *tail_skb = head_skb;
+
+ while (bytelimit > 0) {
+ skb = q->dequeue(q);
+ if (!skb)
+ break;
+
+ bytelimit -= skb->len; /* covers GSO len */
+ skb = validate_xmit_skb(skb, qdisc_dev(q));
+ if (!skb)
+ break;
+
+ while (tail_skb->next) /* GSO list goto tail */
+ tail_skb = tail_skb->next;
+
+ tail_skb->next = skb;
+ tail_skb = skb;
+ }
+
+ return head_skb;
+}
+
+/* Note that dequeue_skb can possibly return a SKB list (via skb->next).
+ * A requeued skb (via q->gso_skb) can also be a SKB list.
+ */
static inline struct sk_buff *dequeue_skb(struct Qdisc *q)
{
struct sk_buff *skb = q->gso_skb;
@@ -70,10 +99,17 @@ static inline struct sk_buff *dequeue_skb(struct Qdisc *q)
} else
skb = NULL;
} else {
- if (!(q->flags & TCQ_F_ONETXQUEUE) || !netif_xmit_frozen_or_stopped(txq)) {
+ if (!(q->flags & TCQ_F_ONETXQUEUE) ||
+ !netif_xmit_frozen_or_stopped(txq)) {
+ int bytelimit = qdisc_avail_bulklimit(txq);
+
skb = q->dequeue(q);
- if (skb)
+ if (skb) {
+ bytelimit -= skb->len;
skb = validate_xmit_skb(skb, qdisc_dev(q));
+ }
+ if (skb && qdisc_may_bulk(q))
+ skb = try_bulk_dequeue_skb(q, skb, bytelimit);
}
}