diff options
Diffstat (limited to 'target/linux/generic/patches-4.4/031-net_sched-update-hierarchical-backlog-too.patch')
-rw-r--r-- | target/linux/generic/patches-4.4/031-net_sched-update-hierarchical-backlog-too.patch | 647 |
1 files changed, 647 insertions, 0 deletions
diff --git a/target/linux/generic/patches-4.4/031-net_sched-update-hierarchical-backlog-too.patch b/target/linux/generic/patches-4.4/031-net_sched-update-hierarchical-backlog-too.patch new file mode 100644 index 0000000000..eb34eea4a6 --- /dev/null +++ b/target/linux/generic/patches-4.4/031-net_sched-update-hierarchical-backlog-too.patch @@ -0,0 +1,647 @@ +From: WANG Cong <xiyou.wangcong@gmail.com> +Date: Thu, 25 Feb 2016 14:55:01 -0800 +Subject: [PATCH] net_sched: update hierarchical backlog too + +When the bottom qdisc decides to, for example, drop some packet, +it calls qdisc_tree_decrease_qlen() to update the queue length +for all its ancestors, we need to update the backlog too to +keep the stats on root qdisc accurate. + +Cc: Jamal Hadi Salim <jhs@mojatatu.com> +Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> +Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + +--- a/include/net/codel.h ++++ b/include/net/codel.h +@@ -162,12 +162,14 @@ struct codel_vars { + * struct codel_stats - contains codel shared variables and stats + * @maxpacket: largest packet we've seen so far + * @drop_count: temp count of dropped packets in dequeue() ++ * @drop_len: bytes of dropped packets in dequeue() + * ecn_mark: number of packets we ECN marked instead of dropping + * ce_mark: number of packets CE marked because sojourn time was above ce_threshold + */ + struct codel_stats { + u32 maxpacket; + u32 drop_count; ++ u32 drop_len; + u32 ecn_mark; + u32 ce_mark; + }; +@@ -308,6 +310,7 @@ static struct sk_buff *codel_dequeue(str + vars->rec_inv_sqrt); + goto end; + } ++ stats->drop_len += qdisc_pkt_len(skb); + qdisc_drop(skb, sch); + stats->drop_count++; + skb = dequeue_func(vars, sch); +@@ -330,6 +333,7 @@ static struct sk_buff *codel_dequeue(str + if (params->ecn && INET_ECN_set_ce(skb)) { + stats->ecn_mark++; + } else { ++ stats->drop_len += qdisc_pkt_len(skb); + qdisc_drop(skb, sch); + stats->drop_count++; + +--- a/include/net/sch_generic.h ++++ b/include/net/sch_generic.h +@@ -396,7 +396,8 @@ struct Qdisc *dev_graft_qdisc(struct net + struct Qdisc *qdisc); + void qdisc_reset(struct Qdisc *qdisc); + void qdisc_destroy(struct Qdisc *qdisc); +-void qdisc_tree_decrease_qlen(struct Qdisc *qdisc, unsigned int n); ++void qdisc_tree_reduce_backlog(struct Qdisc *qdisc, unsigned int n, ++ unsigned int len); + struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, + const struct Qdisc_ops *ops); + struct Qdisc *qdisc_create_dflt(struct netdev_queue *dev_queue, +@@ -707,7 +708,7 @@ static inline struct Qdisc *qdisc_replac + old = *pold; + *pold = new; + if (old != NULL) { +- qdisc_tree_decrease_qlen(old, old->q.qlen); ++ qdisc_tree_reduce_backlog(old, old->q.qlen, old->qstats.backlog); + qdisc_reset(old); + } + sch_tree_unlock(sch); +--- a/net/sched/sch_api.c ++++ b/net/sched/sch_api.c +@@ -744,14 +744,15 @@ static u32 qdisc_alloc_handle(struct net + return 0; + } + +-void qdisc_tree_decrease_qlen(struct Qdisc *sch, unsigned int n) ++void qdisc_tree_reduce_backlog(struct Qdisc *sch, unsigned int n, ++ unsigned int len) + { + const struct Qdisc_class_ops *cops; + unsigned long cl; + u32 parentid; + int drops; + +- if (n == 0) ++ if (n == 0 && len == 0) + return; + drops = max_t(int, n, 0); + rcu_read_lock(); +@@ -774,11 +775,12 @@ void qdisc_tree_decrease_qlen(struct Qdi + cops->put(sch, cl); + } + sch->q.qlen -= n; ++ sch->qstats.backlog -= len; + __qdisc_qstats_drop(sch, drops); + } + rcu_read_unlock(); + } +-EXPORT_SYMBOL(qdisc_tree_decrease_qlen); ++EXPORT_SYMBOL(qdisc_tree_reduce_backlog); + + static void notify_and_destroy(struct net *net, struct sk_buff *skb, + struct nlmsghdr *n, u32 clid, +--- a/net/sched/sch_cbq.c ++++ b/net/sched/sch_cbq.c +@@ -1909,7 +1909,7 @@ static int cbq_delete(struct Qdisc *sch, + { + struct cbq_sched_data *q = qdisc_priv(sch); + struct cbq_class *cl = (struct cbq_class *)arg; +- unsigned int qlen; ++ unsigned int qlen, backlog; + + if (cl->filters || cl->children || cl == &q->link) + return -EBUSY; +@@ -1917,8 +1917,9 @@ static int cbq_delete(struct Qdisc *sch, + sch_tree_lock(sch); + + qlen = cl->q->q.qlen; ++ backlog = cl->q->qstats.backlog; + qdisc_reset(cl->q); +- qdisc_tree_decrease_qlen(cl->q, qlen); ++ qdisc_tree_reduce_backlog(cl->q, qlen, backlog); + + if (cl->next_alive) + cbq_deactivate_class(cl); +--- a/net/sched/sch_choke.c ++++ b/net/sched/sch_choke.c +@@ -128,8 +128,8 @@ static void choke_drop_by_idx(struct Qdi + choke_zap_tail_holes(q); + + qdisc_qstats_backlog_dec(sch, skb); ++ qdisc_tree_reduce_backlog(sch, 1, qdisc_pkt_len(skb)); + qdisc_drop(skb, sch); +- qdisc_tree_decrease_qlen(sch, 1); + --sch->q.qlen; + } + +@@ -456,6 +456,7 @@ static int choke_change(struct Qdisc *sc + old = q->tab; + if (old) { + unsigned int oqlen = sch->q.qlen, tail = 0; ++ unsigned dropped = 0; + + while (q->head != q->tail) { + struct sk_buff *skb = q->tab[q->head]; +@@ -467,11 +468,12 @@ static int choke_change(struct Qdisc *sc + ntab[tail++] = skb; + continue; + } ++ dropped += qdisc_pkt_len(skb); + qdisc_qstats_backlog_dec(sch, skb); + --sch->q.qlen; + qdisc_drop(skb, sch); + } +- qdisc_tree_decrease_qlen(sch, oqlen - sch->q.qlen); ++ qdisc_tree_reduce_backlog(sch, oqlen - sch->q.qlen, dropped); + q->head = 0; + q->tail = tail; + } +--- a/net/sched/sch_codel.c ++++ b/net/sched/sch_codel.c +@@ -79,12 +79,13 @@ static struct sk_buff *codel_qdisc_deque + + skb = codel_dequeue(sch, &q->params, &q->vars, &q->stats, dequeue); + +- /* We cant call qdisc_tree_decrease_qlen() if our qlen is 0, ++ /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0, + * or HTB crashes. Defer it for next round. + */ + if (q->stats.drop_count && sch->q.qlen) { +- qdisc_tree_decrease_qlen(sch, q->stats.drop_count); ++ qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len); + q->stats.drop_count = 0; ++ q->stats.drop_len = 0; + } + if (skb) + qdisc_bstats_update(sch, skb); +@@ -116,7 +117,7 @@ static int codel_change(struct Qdisc *sc + { + struct codel_sched_data *q = qdisc_priv(sch); + struct nlattr *tb[TCA_CODEL_MAX + 1]; +- unsigned int qlen; ++ unsigned int qlen, dropped = 0; + int err; + + if (!opt) +@@ -156,10 +157,11 @@ static int codel_change(struct Qdisc *sc + while (sch->q.qlen > sch->limit) { + struct sk_buff *skb = __skb_dequeue(&sch->q); + ++ dropped += qdisc_pkt_len(skb); + qdisc_qstats_backlog_dec(sch, skb); + qdisc_drop(skb, sch); + } +- qdisc_tree_decrease_qlen(sch, qlen - sch->q.qlen); ++ qdisc_tree_reduce_backlog(sch, qlen - sch->q.qlen, dropped); + + sch_tree_unlock(sch); + return 0; +--- a/net/sched/sch_drr.c ++++ b/net/sched/sch_drr.c +@@ -53,9 +53,10 @@ static struct drr_class *drr_find_class( + static void drr_purge_queue(struct drr_class *cl) + { + unsigned int len = cl->qdisc->q.qlen; ++ unsigned int backlog = cl->qdisc->qstats.backlog; + + qdisc_reset(cl->qdisc); +- qdisc_tree_decrease_qlen(cl->qdisc, len); ++ qdisc_tree_reduce_backlog(cl->qdisc, len, backlog); + } + + static const struct nla_policy drr_policy[TCA_DRR_MAX + 1] = { +--- a/net/sched/sch_fq.c ++++ b/net/sched/sch_fq.c +@@ -662,6 +662,7 @@ static int fq_change(struct Qdisc *sch, + struct fq_sched_data *q = qdisc_priv(sch); + struct nlattr *tb[TCA_FQ_MAX + 1]; + int err, drop_count = 0; ++ unsigned drop_len = 0; + u32 fq_log; + + if (!opt) +@@ -736,10 +737,11 @@ static int fq_change(struct Qdisc *sch, + + if (!skb) + break; ++ drop_len += qdisc_pkt_len(skb); + kfree_skb(skb); + drop_count++; + } +- qdisc_tree_decrease_qlen(sch, drop_count); ++ qdisc_tree_reduce_backlog(sch, drop_count, drop_len); + + sch_tree_unlock(sch); + return err; +--- a/net/sched/sch_fq_codel.c ++++ b/net/sched/sch_fq_codel.c +@@ -175,7 +175,7 @@ static unsigned int fq_codel_qdisc_drop( + static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch) + { + struct fq_codel_sched_data *q = qdisc_priv(sch); +- unsigned int idx; ++ unsigned int idx, prev_backlog; + struct fq_codel_flow *flow; + int uninitialized_var(ret); + +@@ -203,6 +203,7 @@ static int fq_codel_enqueue(struct sk_bu + if (++sch->q.qlen <= sch->limit) + return NET_XMIT_SUCCESS; + ++ prev_backlog = sch->qstats.backlog; + q->drop_overlimit++; + /* Return Congestion Notification only if we dropped a packet + * from this flow. +@@ -211,7 +212,7 @@ static int fq_codel_enqueue(struct sk_bu + return NET_XMIT_CN; + + /* As we dropped a packet, better let upper stack know this */ +- qdisc_tree_decrease_qlen(sch, 1); ++ qdisc_tree_reduce_backlog(sch, 1, prev_backlog - sch->qstats.backlog); + return NET_XMIT_SUCCESS; + } + +@@ -241,6 +242,7 @@ static struct sk_buff *fq_codel_dequeue( + struct fq_codel_flow *flow; + struct list_head *head; + u32 prev_drop_count, prev_ecn_mark; ++ unsigned int prev_backlog; + + begin: + head = &q->new_flows; +@@ -259,6 +261,7 @@ begin: + + prev_drop_count = q->cstats.drop_count; + prev_ecn_mark = q->cstats.ecn_mark; ++ prev_backlog = sch->qstats.backlog; + + skb = codel_dequeue(sch, &q->cparams, &flow->cvars, &q->cstats, + dequeue); +@@ -276,12 +279,14 @@ begin: + } + qdisc_bstats_update(sch, skb); + flow->deficit -= qdisc_pkt_len(skb); +- /* We cant call qdisc_tree_decrease_qlen() if our qlen is 0, ++ /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0, + * or HTB crashes. Defer it for next round. + */ + if (q->cstats.drop_count && sch->q.qlen) { +- qdisc_tree_decrease_qlen(sch, q->cstats.drop_count); ++ qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, ++ q->cstats.drop_len); + q->cstats.drop_count = 0; ++ q->cstats.drop_len = 0; + } + return skb; + } +@@ -372,11 +377,13 @@ static int fq_codel_change(struct Qdisc + while (sch->q.qlen > sch->limit) { + struct sk_buff *skb = fq_codel_dequeue(sch); + ++ q->cstats.drop_len += qdisc_pkt_len(skb); + kfree_skb(skb); + q->cstats.drop_count++; + } +- qdisc_tree_decrease_qlen(sch, q->cstats.drop_count); ++ qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, q->cstats.drop_len); + q->cstats.drop_count = 0; ++ q->cstats.drop_len = 0; + + sch_tree_unlock(sch); + return 0; +--- a/net/sched/sch_hfsc.c ++++ b/net/sched/sch_hfsc.c +@@ -895,9 +895,10 @@ static void + hfsc_purge_queue(struct Qdisc *sch, struct hfsc_class *cl) + { + unsigned int len = cl->qdisc->q.qlen; ++ unsigned int backlog = cl->qdisc->qstats.backlog; + + qdisc_reset(cl->qdisc); +- qdisc_tree_decrease_qlen(cl->qdisc, len); ++ qdisc_tree_reduce_backlog(cl->qdisc, len, backlog); + } + + static void +--- a/net/sched/sch_hhf.c ++++ b/net/sched/sch_hhf.c +@@ -382,6 +382,7 @@ static int hhf_enqueue(struct sk_buff *s + struct hhf_sched_data *q = qdisc_priv(sch); + enum wdrr_bucket_idx idx; + struct wdrr_bucket *bucket; ++ unsigned int prev_backlog; + + idx = hhf_classify(skb, sch); + +@@ -409,6 +410,7 @@ static int hhf_enqueue(struct sk_buff *s + if (++sch->q.qlen <= sch->limit) + return NET_XMIT_SUCCESS; + ++ prev_backlog = sch->qstats.backlog; + q->drop_overlimit++; + /* Return Congestion Notification only if we dropped a packet from this + * bucket. +@@ -417,7 +419,7 @@ static int hhf_enqueue(struct sk_buff *s + return NET_XMIT_CN; + + /* As we dropped a packet, better let upper stack know this. */ +- qdisc_tree_decrease_qlen(sch, 1); ++ qdisc_tree_reduce_backlog(sch, 1, prev_backlog - sch->qstats.backlog); + return NET_XMIT_SUCCESS; + } + +@@ -527,7 +529,7 @@ static int hhf_change(struct Qdisc *sch, + { + struct hhf_sched_data *q = qdisc_priv(sch); + struct nlattr *tb[TCA_HHF_MAX + 1]; +- unsigned int qlen; ++ unsigned int qlen, prev_backlog; + int err; + u64 non_hh_quantum; + u32 new_quantum = q->quantum; +@@ -577,12 +579,14 @@ static int hhf_change(struct Qdisc *sch, + } + + qlen = sch->q.qlen; ++ prev_backlog = sch->qstats.backlog; + while (sch->q.qlen > sch->limit) { + struct sk_buff *skb = hhf_dequeue(sch); + + kfree_skb(skb); + } +- qdisc_tree_decrease_qlen(sch, qlen - sch->q.qlen); ++ qdisc_tree_reduce_backlog(sch, qlen - sch->q.qlen, ++ prev_backlog - sch->qstats.backlog); + + sch_tree_unlock(sch); + return 0; +--- a/net/sched/sch_htb.c ++++ b/net/sched/sch_htb.c +@@ -1265,7 +1265,6 @@ static int htb_delete(struct Qdisc *sch, + { + struct htb_sched *q = qdisc_priv(sch); + struct htb_class *cl = (struct htb_class *)arg; +- unsigned int qlen; + struct Qdisc *new_q = NULL; + int last_child = 0; + +@@ -1285,9 +1284,11 @@ static int htb_delete(struct Qdisc *sch, + sch_tree_lock(sch); + + if (!cl->level) { +- qlen = cl->un.leaf.q->q.qlen; ++ unsigned int qlen = cl->un.leaf.q->q.qlen; ++ unsigned int backlog = cl->un.leaf.q->qstats.backlog; ++ + qdisc_reset(cl->un.leaf.q); +- qdisc_tree_decrease_qlen(cl->un.leaf.q, qlen); ++ qdisc_tree_reduce_backlog(cl->un.leaf.q, qlen, backlog); + } + + /* delete from hash and active; remainder in destroy_class */ +@@ -1421,10 +1422,11 @@ static int htb_change_class(struct Qdisc + sch_tree_lock(sch); + if (parent && !parent->level) { + unsigned int qlen = parent->un.leaf.q->q.qlen; ++ unsigned int backlog = parent->un.leaf.q->qstats.backlog; + + /* turn parent into inner node */ + qdisc_reset(parent->un.leaf.q); +- qdisc_tree_decrease_qlen(parent->un.leaf.q, qlen); ++ qdisc_tree_reduce_backlog(parent->un.leaf.q, qlen, backlog); + qdisc_destroy(parent->un.leaf.q); + if (parent->prio_activity) + htb_deactivate(q, parent); +--- a/net/sched/sch_multiq.c ++++ b/net/sched/sch_multiq.c +@@ -218,7 +218,8 @@ static int multiq_tune(struct Qdisc *sch + if (q->queues[i] != &noop_qdisc) { + struct Qdisc *child = q->queues[i]; + q->queues[i] = &noop_qdisc; +- qdisc_tree_decrease_qlen(child, child->q.qlen); ++ qdisc_tree_reduce_backlog(child, child->q.qlen, ++ child->qstats.backlog); + qdisc_destroy(child); + } + } +@@ -238,8 +239,9 @@ static int multiq_tune(struct Qdisc *sch + q->queues[i] = child; + + if (old != &noop_qdisc) { +- qdisc_tree_decrease_qlen(old, +- old->q.qlen); ++ qdisc_tree_reduce_backlog(old, ++ old->q.qlen, ++ old->qstats.backlog); + qdisc_destroy(old); + } + sch_tree_unlock(sch); +--- a/net/sched/sch_netem.c ++++ b/net/sched/sch_netem.c +@@ -598,7 +598,8 @@ deliver: + if (unlikely(err != NET_XMIT_SUCCESS)) { + if (net_xmit_drop_count(err)) { + qdisc_qstats_drop(sch); +- qdisc_tree_decrease_qlen(sch, 1); ++ qdisc_tree_reduce_backlog(sch, 1, ++ qdisc_pkt_len(skb)); + } + } + goto tfifo_dequeue; +--- a/net/sched/sch_pie.c ++++ b/net/sched/sch_pie.c +@@ -183,7 +183,7 @@ static int pie_change(struct Qdisc *sch, + { + struct pie_sched_data *q = qdisc_priv(sch); + struct nlattr *tb[TCA_PIE_MAX + 1]; +- unsigned int qlen; ++ unsigned int qlen, dropped = 0; + int err; + + if (!opt) +@@ -232,10 +232,11 @@ static int pie_change(struct Qdisc *sch, + while (sch->q.qlen > sch->limit) { + struct sk_buff *skb = __skb_dequeue(&sch->q); + ++ dropped += qdisc_pkt_len(skb); + qdisc_qstats_backlog_dec(sch, skb); + qdisc_drop(skb, sch); + } +- qdisc_tree_decrease_qlen(sch, qlen - sch->q.qlen); ++ qdisc_tree_reduce_backlog(sch, qlen - sch->q.qlen, dropped); + + sch_tree_unlock(sch); + return 0; +--- a/net/sched/sch_prio.c ++++ b/net/sched/sch_prio.c +@@ -191,7 +191,7 @@ static int prio_tune(struct Qdisc *sch, + struct Qdisc *child = q->queues[i]; + q->queues[i] = &noop_qdisc; + if (child != &noop_qdisc) { +- qdisc_tree_decrease_qlen(child, child->q.qlen); ++ qdisc_tree_reduce_backlog(child, child->q.qlen, child->qstats.backlog); + qdisc_destroy(child); + } + } +@@ -210,8 +210,9 @@ static int prio_tune(struct Qdisc *sch, + q->queues[i] = child; + + if (old != &noop_qdisc) { +- qdisc_tree_decrease_qlen(old, +- old->q.qlen); ++ qdisc_tree_reduce_backlog(old, ++ old->q.qlen, ++ old->qstats.backlog); + qdisc_destroy(old); + } + sch_tree_unlock(sch); +--- a/net/sched/sch_qfq.c ++++ b/net/sched/sch_qfq.c +@@ -220,9 +220,10 @@ static struct qfq_class *qfq_find_class( + static void qfq_purge_queue(struct qfq_class *cl) + { + unsigned int len = cl->qdisc->q.qlen; ++ unsigned int backlog = cl->qdisc->qstats.backlog; + + qdisc_reset(cl->qdisc); +- qdisc_tree_decrease_qlen(cl->qdisc, len); ++ qdisc_tree_reduce_backlog(cl->qdisc, len, backlog); + } + + static const struct nla_policy qfq_policy[TCA_QFQ_MAX + 1] = { +--- a/net/sched/sch_red.c ++++ b/net/sched/sch_red.c +@@ -210,7 +210,8 @@ static int red_change(struct Qdisc *sch, + q->flags = ctl->flags; + q->limit = ctl->limit; + if (child) { +- qdisc_tree_decrease_qlen(q->qdisc, q->qdisc->q.qlen); ++ qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen, ++ q->qdisc->qstats.backlog); + qdisc_destroy(q->qdisc); + q->qdisc = child; + } +--- a/net/sched/sch_sfb.c ++++ b/net/sched/sch_sfb.c +@@ -510,7 +510,8 @@ static int sfb_change(struct Qdisc *sch, + + sch_tree_lock(sch); + +- qdisc_tree_decrease_qlen(q->qdisc, q->qdisc->q.qlen); ++ qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen, ++ q->qdisc->qstats.backlog); + qdisc_destroy(q->qdisc); + q->qdisc = child; + +--- a/net/sched/sch_sfq.c ++++ b/net/sched/sch_sfq.c +@@ -346,7 +346,7 @@ static int + sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) + { + struct sfq_sched_data *q = qdisc_priv(sch); +- unsigned int hash; ++ unsigned int hash, dropped; + sfq_index x, qlen; + struct sfq_slot *slot; + int uninitialized_var(ret); +@@ -461,7 +461,7 @@ enqueue: + return NET_XMIT_SUCCESS; + + qlen = slot->qlen; +- sfq_drop(sch); ++ dropped = sfq_drop(sch); + /* Return Congestion Notification only if we dropped a packet + * from this flow. + */ +@@ -469,7 +469,7 @@ enqueue: + return NET_XMIT_CN; + + /* As we dropped a packet, better let upper stack know this */ +- qdisc_tree_decrease_qlen(sch, 1); ++ qdisc_tree_reduce_backlog(sch, 1, dropped); + return NET_XMIT_SUCCESS; + } + +@@ -537,6 +537,7 @@ static void sfq_rehash(struct Qdisc *sch + struct sfq_slot *slot; + struct sk_buff_head list; + int dropped = 0; ++ unsigned int drop_len = 0; + + __skb_queue_head_init(&list); + +@@ -565,6 +566,7 @@ static void sfq_rehash(struct Qdisc *sch + if (x >= SFQ_MAX_FLOWS) { + drop: + qdisc_qstats_backlog_dec(sch, skb); ++ drop_len += qdisc_pkt_len(skb); + kfree_skb(skb); + dropped++; + continue; +@@ -594,7 +596,7 @@ drop: + } + } + sch->q.qlen -= dropped; +- qdisc_tree_decrease_qlen(sch, dropped); ++ qdisc_tree_reduce_backlog(sch, dropped, drop_len); + } + + static void sfq_perturbation(unsigned long arg) +@@ -618,7 +620,7 @@ static int sfq_change(struct Qdisc *sch, + struct sfq_sched_data *q = qdisc_priv(sch); + struct tc_sfq_qopt *ctl = nla_data(opt); + struct tc_sfq_qopt_v1 *ctl_v1 = NULL; +- unsigned int qlen; ++ unsigned int qlen, dropped = 0; + struct red_parms *p = NULL; + + if (opt->nla_len < nla_attr_size(sizeof(*ctl))) +@@ -667,8 +669,8 @@ static int sfq_change(struct Qdisc *sch, + + qlen = sch->q.qlen; + while (sch->q.qlen > q->limit) +- sfq_drop(sch); +- qdisc_tree_decrease_qlen(sch, qlen - sch->q.qlen); ++ dropped += sfq_drop(sch); ++ qdisc_tree_reduce_backlog(sch, qlen - sch->q.qlen, dropped); + + del_timer(&q->perturb_timer); + if (q->perturb_period) { +--- a/net/sched/sch_tbf.c ++++ b/net/sched/sch_tbf.c +@@ -160,6 +160,7 @@ static int tbf_segment(struct sk_buff *s + struct tbf_sched_data *q = qdisc_priv(sch); + struct sk_buff *segs, *nskb; + netdev_features_t features = netif_skb_features(skb); ++ unsigned int len = 0, prev_len = qdisc_pkt_len(skb); + int ret, nb; + + segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK); +@@ -172,6 +173,7 @@ static int tbf_segment(struct sk_buff *s + nskb = segs->next; + segs->next = NULL; + qdisc_skb_cb(segs)->pkt_len = segs->len; ++ len += segs->len; + ret = qdisc_enqueue(segs, q->qdisc); + if (ret != NET_XMIT_SUCCESS) { + if (net_xmit_drop_count(ret)) +@@ -183,7 +185,7 @@ static int tbf_segment(struct sk_buff *s + } + sch->q.qlen += nb; + if (nb > 1) +- qdisc_tree_decrease_qlen(sch, 1 - nb); ++ qdisc_tree_reduce_backlog(sch, 1 - nb, prev_len - len); + consume_skb(skb); + return nb > 0 ? NET_XMIT_SUCCESS : NET_XMIT_DROP; + } +@@ -399,7 +401,8 @@ static int tbf_change(struct Qdisc *sch, + + sch_tree_lock(sch); + if (child) { +- qdisc_tree_decrease_qlen(q->qdisc, q->qdisc->q.qlen); ++ qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen, ++ q->qdisc->qstats.backlog); + qdisc_destroy(q->qdisc); + q->qdisc = child; + } |