From 9f9afec48221fe4a19f84a9341f5b304bf7d7783 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 29 Nov 2006 17:35:18 -0800 Subject: [NET_SCHED]: Set parent classid in default qdiscs Set parent classids in default qdiscs to allow walking up the tree from outside the qdiscs. This is needed by the next patch. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/sched/sch_tbf.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'net/sched/sch_tbf.c') diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index d9a5d298d75..2562a6040b7 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -273,12 +273,14 @@ static void tbf_reset(struct Qdisc* sch) del_timer(&q->wd_timer); } -static struct Qdisc *tbf_create_dflt_qdisc(struct net_device *dev, u32 limit) +static struct Qdisc *tbf_create_dflt_qdisc(struct Qdisc *sch, u32 limit) { - struct Qdisc *q = qdisc_create_dflt(dev, &bfifo_qdisc_ops); + struct Qdisc *q; struct rtattr *rta; int ret; + q = qdisc_create_dflt(sch->dev, &bfifo_qdisc_ops, + TC_H_MAKE(sch->handle, 1)); if (q) { rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), GFP_KERNEL); if (rta) { @@ -341,7 +343,7 @@ static int tbf_change(struct Qdisc* sch, struct rtattr *opt) goto done; if (qopt->limit > 0) { - if ((child = tbf_create_dflt_qdisc(sch->dev, qopt->limit)) == NULL) + if ((child = tbf_create_dflt_qdisc(sch, qopt->limit)) == NULL) goto done; } -- cgit v1.2.3-70-g09d2 From 5e50da01d0ce7ef0ba3ed6cfabd62f327da0aca6 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 29 Nov 2006 17:36:20 -0800 Subject: [NET_SCHED]: Fix endless loops (part 2): "simple" qdiscs Convert the "simple" qdiscs to use qdisc_tree_decrease_qlen() where necessary: - all graft operations - destruction of old child qdiscs in prio, red and tbf change operation - purging of queue in sfq change operation Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/sched/sch_cbq.c | 2 +- net/sched/sch_dsmark.c | 2 +- net/sched/sch_netem.c | 2 +- net/sched/sch_prio.c | 11 ++++++++--- net/sched/sch_red.c | 6 ++++-- net/sched/sch_sfq.c | 3 +++ net/sched/sch_tbf.c | 6 ++++-- 7 files changed, 22 insertions(+), 10 deletions(-) (limited to 'net/sched/sch_tbf.c') diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 908b10d0d61..ba82dfab604 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -1687,7 +1687,7 @@ static int cbq_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, sch_tree_lock(sch); *old = cl->q; cl->q = new; - sch->q.qlen -= (*old)->q.qlen; + qdisc_tree_decrease_qlen(*old, (*old)->q.qlen); qdisc_reset(*old); sch_tree_unlock(sch); diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c index afedd92b749..d5421816f00 100644 --- a/net/sched/sch_dsmark.c +++ b/net/sched/sch_dsmark.c @@ -96,8 +96,8 @@ static int dsmark_graft(struct Qdisc *sch, unsigned long arg, sch_tree_lock(sch); *old = xchg(&p->q, new); + qdisc_tree_decrease_qlen(*old, (*old)->q.qlen); qdisc_reset(*old); - sch->q.qlen = 0; sch_tree_unlock(sch); return 0; diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 90aeeb7b716..672c3544579 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -662,8 +662,8 @@ static int netem_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, sch_tree_lock(sch); *old = xchg(&q->qdisc, new); + qdisc_tree_decrease_qlen(*old, (*old)->q.qlen); qdisc_reset(*old); - sch->q.qlen = 0; sch_tree_unlock(sch); return 0; diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 3fc0c0fa5bc..2567b4c96c1 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -222,8 +222,10 @@ static int prio_tune(struct Qdisc *sch, struct rtattr *opt) for (i=q->bands; iqueues[i], &noop_qdisc); - if (child != &noop_qdisc) + if (child != &noop_qdisc) { + qdisc_tree_decrease_qlen(child, child->q.qlen); qdisc_destroy(child); + } } sch_tree_unlock(sch); @@ -236,8 +238,11 @@ static int prio_tune(struct Qdisc *sch, struct rtattr *opt) sch_tree_lock(sch); child = xchg(&q->queues[i], child); - if (child != &noop_qdisc) + if (child != &noop_qdisc) { + qdisc_tree_decrease_qlen(child, + child->q.qlen); qdisc_destroy(child); + } sch_tree_unlock(sch); } } @@ -295,7 +300,7 @@ static int prio_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, sch_tree_lock(sch); *old = q->queues[band]; q->queues[band] = new; - sch->q.qlen -= (*old)->q.qlen; + qdisc_tree_decrease_qlen(*old, (*old)->q.qlen); qdisc_reset(*old); sch_tree_unlock(sch); diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index ee66c5ca80c..acddad08850 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -229,8 +229,10 @@ static int red_change(struct Qdisc *sch, struct rtattr *opt) sch_tree_lock(sch); q->flags = ctl->flags; q->limit = ctl->limit; - if (child) + if (child) { + qdisc_tree_decrease_qlen(q->qdisc, q->qdisc->q.qlen); qdisc_destroy(xchg(&q->qdisc, child)); + } red_set_parms(&q->parms, ctl->qth_min, ctl->qth_max, ctl->Wlog, ctl->Plog, ctl->Scell_log, @@ -308,8 +310,8 @@ static int red_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, sch_tree_lock(sch); *old = xchg(&q->qdisc, new); + qdisc_tree_decrease_qlen(*old, (*old)->q.qlen); qdisc_reset(*old); - sch->q.qlen = 0; sch_tree_unlock(sch); return 0; } diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index d0d6e595a78..459cda258a5 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -393,6 +393,7 @@ static int sfq_change(struct Qdisc *sch, struct rtattr *opt) { struct sfq_sched_data *q = qdisc_priv(sch); struct tc_sfq_qopt *ctl = RTA_DATA(opt); + unsigned int qlen; if (opt->rta_len < RTA_LENGTH(sizeof(*ctl))) return -EINVAL; @@ -403,8 +404,10 @@ static int sfq_change(struct Qdisc *sch, struct rtattr *opt) if (ctl->limit) q->limit = min_t(u32, ctl->limit, SFQ_DEPTH); + qlen = sch->q.qlen; while (sch->q.qlen >= q->limit-1) sfq_drop(sch); + qdisc_tree_decrease_qlen(sch, qlen - sch->q.qlen); del_timer(&q->perturb_timer); if (q->perturb_period) { diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index 2562a6040b7..23b7624354f 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -348,8 +348,10 @@ static int tbf_change(struct Qdisc* sch, struct rtattr *opt) } sch_tree_lock(sch); - if (child) + if (child) { + qdisc_tree_decrease_qlen(q->qdisc, q->qdisc->q.qlen); qdisc_destroy(xchg(&q->qdisc, child)); + } q->limit = qopt->limit; q->mtu = qopt->mtu; q->max_size = max_size; @@ -451,8 +453,8 @@ static int tbf_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, sch_tree_lock(sch); *old = xchg(&q->qdisc, new); + qdisc_tree_decrease_qlen(*old, (*old)->q.qlen); qdisc_reset(*old); - sch->q.qlen = 0; sch_tree_unlock(sch); return 0; -- cgit v1.2.3-70-g09d2 From e488eafcc50be296f0d1e1fd67c6b5d865183011 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 29 Nov 2006 17:37:42 -0800 Subject: [NET_SCHED]: Fix endless loops (part 5): netem/tbf/hfsc ->requeue failures When peeking at the next packet in a child qdisc by calling dequeue/requeue, the upper qdisc qlen counter may get out of sync in case the requeue fails. The qdisc and the child qdisc both have their counter decremented, but since no packet is given to the upper qdisc it won't decrement its counter itself. requeue should not fail, so this is mostly for "correctness". Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/sched/sch_hfsc.c | 1 + net/sched/sch_netem.c | 5 +---- net/sched/sch_tbf.c | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) (limited to 'net/sched/sch_tbf.c') diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 2d437447e08..6eefa699577 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -946,6 +946,7 @@ qdisc_peek_len(struct Qdisc *sch) if (unlikely(sch->ops->requeue(skb, sch) != NET_XMIT_SUCCESS)) { if (net_ratelimit()) printk("qdisc_peek_len: failed to requeue\n"); + qdisc_tree_decrease_qlen(sch, 1); return 0; } return len; diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 672c3544579..79542af9dab 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -287,13 +287,10 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch) psched_tdiff_t delay = PSCHED_TDIFF(cb->time_to_send, now); if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) { + qdisc_tree_decrease_qlen(q->qdisc, 1); sch->qstats.drops++; - - /* After this qlen is confused */ printk(KERN_ERR "netem: queue discpline %s could not requeue\n", q->qdisc->ops->id); - - sch->q.qlen--; } mod_timer(&q->timer, jiffies + PSCHED_US2JIFFIE(delay)); diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index 23b7624354f..ed9b6d93854 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -250,7 +250,7 @@ static struct sk_buff *tbf_dequeue(struct Qdisc* sch) if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) { /* When requeue fails skb is dropped */ - sch->q.qlen--; + qdisc_tree_decrease_qlen(q->qdisc, 1); sch->qstats.drops++; } -- cgit v1.2.3-70-g09d2