From: Alberto Bertogli <hidden> Date: 2002-07-22 03:57:42
Hi!
This is a (really early) bug report on one i just caught when setting a
filter for traffic control.
It makes the machine to frozen solid, without even an oops. Keyboard is not
responsive (neither sysrq nor the num/caps/screen lock leds), everything
seems quite dead.
It's fully reproducible, i tried both 2.4.13 and 2.4.19-rc3 (the last one
with htb patches, but i don't think it's related).
The machine is an iPII with 256Mb RAM, it has been working well for about a
year and a half.
Basically a main cbq class with two (also cbq) leaves:
tc qdisc add dev eth1 root handle 20:0 cbq bandwidth 100Mbit avpkt 1000
tc class add dev eth1 parent 20:0 classid 20:1 $EST cbq bandwidth 100Mbit \
rate 2800Kbit bounded isolated $CBQ_PARAMS
($EST is empty and $CBQ_PARAMS is "allot 1514 avpkt 1000")
Both leaves are basically the same, just a cbq class with parent 20:1 and
then sfq attached as qdisc; really simple. I have another set pretty much
like this one, only with one leave, on eth0.
The command which frozes the machine is
tc filter add dev eth1 parent 20:0 protocol ip handle 5 fw classid 20:0
I know it's weird, but anyway i don't think that a lockup is the error the
user deserves =)
Setting classid 20:X (X != 0) works as expected.
As this is quite early (just hit it) i don't know if i can reproduce it
using another scenario.
Tomorrow i'll try to dig into it on a testing machine and i'll post the
results.
Thanks,
Alberto
On Tue, Jul 23, 2002 at 12:46:44AM +0400, kuznet@ms2.inr.ac.ru wrote:
Hello!
quoted
I know it's weird, but anyway i don't think that a lockup is the error the
user deserves =)
You really deserved this.
I also reported this - should tc check this? Or the kernel? I think each
qdisc implements its own filter hooks and rules, so tc may be a nice place
to do at least simple checking, although it cannot easily spot complicated
loops.
if ((cl = (void*)res.class) == NULL) {
if (TC_H_MAJ(res.classid))
cl = cbq_class_lookup(q, res.classid);
else if ((cl = defmap[res.classid&TC_PRIO_MAX]) == NULL
cl = defmap[TC_PRIO_BESTEFFORT];
if (cl == NULL || cl->level >= head->level)
goto fallback;
}
Aren't the last 2 lines meant to prevent this from happening?
(net/sched/sch_cbq.c).
Please tell me your ideas, I hope to fix this. I know people who've had this
happen by accident and they really hate it.
Regards,
bert
--
http://www.PowerDNS.com Versatile DNS Software & Services
http://www.tk the dot in .tk
http://lartc.org Linux Advanced Routing & Traffic Control HOWTO
On Tue, Jul 23, 2002 at 02:57:14AM +0400, kuznet@ms2.inr.ac.ru wrote:
quoted
Please tell me your ideas,
Well, before all figure out where it deadlocks.
I tried this in User Mode Linux and after some help by Jeff, I figured out
that the deadlock is within cbq_classify() in sch_cbq.c. The problem is that
the 'no upwards classifying' check:
if (cl == NULL || cl->level >= head->level)
goto fallback;
is only applied if the classifyer did not return an answer:
if (!head->filter_list || (result = tc_classify(skb,
head->filter_list, &res)) < 0)
goto fallback;
if ((cl = (void*)res.class) == NULL) {
if (TC_H_MAJ(res.classid))
cl = cbq_class_lookup(q, res.classid);
else if ((cl = defmap[res.classid&TC_PRIO_MAX]) == NULL)
cl = defmap[TC_PRIO_BESTEFFORT];
if (cl == NULL || cl->level >= head->level)
goto fallback;
}
However, with the commandlines specified, the classifier returns.. head, and
the loop prevention test is never performed.
Suggested fix is to move down the test to the end of the loop so it is
always invoked before iterating. Another test that might sense is to see if
head and cl differ before moving on. A loop is guaranteed in that case.
I can build this into a patch if needed, but I suspect Alexey will want to
write his own.
Thanks for your time.
Regards,
bert
--
http://www.PowerDNS.com Versatile DNS Software & Services
http://www.tk the dot in .tk
http://lartc.org Linux Advanced Routing & Traffic Control HOWTO
In the last command, cbq_bind_filter is called with parent==0:
Damn, indeed. Congratulations!
It should look like:
+ if (p == NULL)
+ p = &q->link;
if (cl) {
- if (p && p->level <= cl->level)
+ if (p->level <= cl->level)
return 0;
cl->filters++;
Alexey