diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2012-01-02 19:00:11 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-01-03 13:03:20 -0500 |
commit | fa0f5aa74316c636427ac92dad0bc5714c34ca17 (patch) | |
tree | 64fcb6a7f4043cdaea4a13b730a1ca651190e314 /net/sched | |
parent | d32ae76f2b776347051cf821ebe690602e38dfc7 (diff) |
net_sched: qdisc_alloc_handle() can be too slow
When trying to allocate ~32768 qdiscs using autohandle mechanism, we can
fill the space managed by kernel (handles in [8000-FFFF]:0000 range)
But O(N^2) qdisc_alloc_handle() loops 0x10000 times instead of 0x8000
time tc add qdisc add dev eth0 parent 10:7fff pfifo limit 10
RTNETLINK answers: Cannot allocate memory
real 1m54.826s
user 0m0.000s
sys 0m0.004s
INFO: rcu_sched_state detected stall on CPU 0 (t=60000 jiffies)
Half number of loops, and add a cond_resched() call.
We hold rtnl at this point.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
CC: Dave Taht <dave.taht@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sched')
-rw-r--r-- | net/sched/sch_api.c | 14 |
1 files changed, 9 insertions, 5 deletions
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index dca6c1a576f7..3d8981fde301 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c | |||
@@ -618,20 +618,24 @@ void qdisc_class_hash_remove(struct Qdisc_class_hash *clhash, | |||
618 | } | 618 | } |
619 | EXPORT_SYMBOL(qdisc_class_hash_remove); | 619 | EXPORT_SYMBOL(qdisc_class_hash_remove); |
620 | 620 | ||
621 | /* Allocate an unique handle from space managed by kernel */ | 621 | /* Allocate an unique handle from space managed by kernel |
622 | 622 | * Possible range is [8000-FFFF]:0000 (0x8000 values) | |
623 | */ | ||
623 | static u32 qdisc_alloc_handle(struct net_device *dev) | 624 | static u32 qdisc_alloc_handle(struct net_device *dev) |
624 | { | 625 | { |
625 | int i = 0x10000; | 626 | int i = 0x8000; |
626 | static u32 autohandle = TC_H_MAKE(0x80000000U, 0); | 627 | static u32 autohandle = TC_H_MAKE(0x80000000U, 0); |
627 | 628 | ||
628 | do { | 629 | do { |
629 | autohandle += TC_H_MAKE(0x10000U, 0); | 630 | autohandle += TC_H_MAKE(0x10000U, 0); |
630 | if (autohandle == TC_H_MAKE(TC_H_ROOT, 0)) | 631 | if (autohandle == TC_H_MAKE(TC_H_ROOT, 0)) |
631 | autohandle = TC_H_MAKE(0x80000000U, 0); | 632 | autohandle = TC_H_MAKE(0x80000000U, 0); |
632 | } while (qdisc_lookup(dev, autohandle) && --i > 0); | 633 | if (!qdisc_lookup(dev, autohandle)) |
634 | return autohandle; | ||
635 | cond_resched(); | ||
636 | } while (--i > 0); | ||
633 | 637 | ||
634 | return i > 0 ? autohandle : 0; | 638 | return 0; |
635 | } | 639 | } |
636 | 640 | ||
637 | void qdisc_tree_decrease_qlen(struct Qdisc *sch, unsigned int n) | 641 | void qdisc_tree_decrease_qlen(struct Qdisc *sch, unsigned int n) |