* 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;
 };
                                                                  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);
                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++;
 
 
                              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,
        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);
 
        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();
                        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,
 
 {
        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;
        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);
 
                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;
 }
 
                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];
                                        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;
                }
 
 
        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);
 {
        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)
        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;
 
 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] = {
 
        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)
 
                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;
 
 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);
 
        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.
                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;
 }
 
        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;
 
        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);
        }
        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;
 }
        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;
 
 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
 
        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);
 
        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.
                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;
 }
 
 {
        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;
        }
 
        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;
 
 {
        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;
 
        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 */
                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);
 
                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);
                }
        }
                                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);
 
                                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;
 
 {
        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)
        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;
 
                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);
                }
        }
                                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);
 
 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] = {
 
        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;
        }
 
 
        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;
 
 
 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);
                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.
         */
                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;
 }
 
        struct sfq_slot *slot;
        struct sk_buff_head list;
        int dropped = 0;
+       unsigned int drop_len = 0;
 
        __skb_queue_head_init(&list);
 
                        if (x >= SFQ_MAX_FLOWS) {
 drop:
                                qdisc_qstats_backlog_dec(sch, skb);
+                               drop_len += qdisc_pkt_len(skb);
                                kfree_skb(skb);
                                dropped++;
                                continue;
                }
        }
        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)
        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)))
 
        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) {
 
        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);
                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))
        }
        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;
 }
 
        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;
        }