Skip to content

Commit

Permalink
net_sched: update hierarchical backlog too
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
congwang authored and davem330 committed Feb 29, 2016
1 parent 86a7996 commit 2ccccf5
Show file tree
Hide file tree
Showing 21 changed files with 91 additions and 49 deletions.
4 changes: 4 additions & 0 deletions include/net/codel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down Expand Up @@ -308,6 +310,7 @@ static struct sk_buff *codel_dequeue(struct Qdisc *sch,
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);
Expand All @@ -330,6 +333,7 @@ static struct sk_buff *codel_dequeue(struct Qdisc *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++;

Expand Down
5 changes: 3 additions & 2 deletions include/net/sch_generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,8 @@ struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue,
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,
Expand Down Expand Up @@ -716,7 +717,7 @@ static inline struct Qdisc *qdisc_replace(struct Qdisc *sch, struct Qdisc *new,
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);
Expand Down
8 changes: 5 additions & 3 deletions net/sched/sch_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -744,14 +744,15 @@ static u32 qdisc_alloc_handle(struct net_device *dev)
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();
Expand All @@ -774,11 +775,12 @@ void qdisc_tree_decrease_qlen(struct Qdisc *sch, unsigned int n)
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,
Expand Down
5 changes: 3 additions & 2 deletions net/sched/sch_cbq.c
Original file line number Diff line number Diff line change
Expand Up @@ -1909,16 +1909,17 @@ static int cbq_delete(struct Qdisc *sch, unsigned long arg)
{
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);
Expand Down
6 changes: 4 additions & 2 deletions net/sched/sch_choke.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ static void choke_drop_by_idx(struct Qdisc *sch, unsigned int idx)
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;
}

Expand Down Expand Up @@ -456,6 +456,7 @@ static int choke_change(struct Qdisc *sch, struct nlattr *opt)
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];
Expand All @@ -467,11 +468,12 @@ static int choke_change(struct Qdisc *sch, struct nlattr *opt)
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;
}
Expand Down
10 changes: 6 additions & 4 deletions net/sched/sch_codel.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,13 @@ static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch)

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);
Expand Down Expand Up @@ -116,7 +117,7 @@ static int codel_change(struct Qdisc *sch, struct nlattr *opt)
{
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)
Expand Down Expand Up @@ -156,10 +157,11 @@ static int codel_change(struct Qdisc *sch, struct nlattr *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;
Expand Down
3 changes: 2 additions & 1 deletion net/sched/sch_drr.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ static struct drr_class *drr_find_class(struct Qdisc *sch, u32 classid)
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] = {
Expand Down
4 changes: 3 additions & 1 deletion net/sched/sch_fq.c
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt)
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)
Expand Down Expand Up @@ -736,10 +737,11 @@ static int fq_change(struct Qdisc *sch, struct nlattr *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;
Expand Down
17 changes: 12 additions & 5 deletions net/sched/sch_fq_codel.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ static unsigned int fq_codel_qdisc_drop(struct Qdisc *sch)
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);

Expand Down Expand Up @@ -203,6 +203,7 @@ static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *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 flow.
Expand All @@ -211,7 +212,7 @@ static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch)
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;
}

Expand Down Expand Up @@ -241,6 +242,7 @@ static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch)
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;
Expand All @@ -259,6 +261,7 @@ static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch)

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);
Expand All @@ -276,12 +279,14 @@ static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch)
}
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;
}
Expand Down Expand Up @@ -372,11 +377,13 @@ static int fq_codel_change(struct Qdisc *sch, struct nlattr *opt)
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;
Expand Down
3 changes: 2 additions & 1 deletion net/sched/sch_hfsc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 7 additions & 3 deletions net/sched/sch_hhf.c
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ static int hhf_enqueue(struct sk_buff *skb, struct Qdisc *sch)
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);

Expand Down Expand Up @@ -409,6 +410,7 @@ static int hhf_enqueue(struct sk_buff *skb, struct Qdisc *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.
Expand All @@ -417,7 +419,7 @@ static int hhf_enqueue(struct sk_buff *skb, struct Qdisc *sch)
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;
}

Expand Down Expand Up @@ -527,7 +529,7 @@ static int hhf_change(struct Qdisc *sch, struct nlattr *opt)
{
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;
Expand Down Expand Up @@ -577,12 +579,14 @@ static int hhf_change(struct Qdisc *sch, struct nlattr *opt)
}

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;
Expand Down
10 changes: 6 additions & 4 deletions net/sched/sch_htb.c
Original file line number Diff line number Diff line change
Expand Up @@ -1265,7 +1265,6 @@ static int htb_delete(struct Qdisc *sch, unsigned long arg)
{
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;

Expand All @@ -1285,9 +1284,11 @@ static int htb_delete(struct Qdisc *sch, unsigned long arg)
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 */
Expand Down Expand Up @@ -1421,10 +1422,11 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
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);
Expand Down
8 changes: 5 additions & 3 deletions net/sched/sch_multiq.c
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@ static int multiq_tune(struct Qdisc *sch, struct nlattr *opt)
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);
}
}
Expand All @@ -238,8 +239,9 @@ static int multiq_tune(struct Qdisc *sch, struct nlattr *opt)
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);
Expand Down
Loading

0 comments on commit 2ccccf5

Please sign in to comment.