aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2006-06-20 02:57:59 -0400
committerDavid S. Miller <davem@davemloft.net>2006-06-20 02:57:59 -0400
commit48d83325b61043e3bbd24dd37b9fe433744cf330 (patch)
tree00dc0682be0f096676ac885152b4ae14c4137338 /net
parentd6cc7f1a3b33c89c91b3dfce1ff053178893470e (diff)
[NET]: Prevent multiple qdisc runs
Having two or more qdisc_run's contend against each other is bad because it can induce packet reordering if the packets have to be requeued. It appears that this is an unintended consequence of relinquinshing the queue lock while transmitting. That in turn is needed for devices that spend a lot of time in their transmit routine. There are no advantages to be had as devices with queues are inherently single-threaded (the loopback device is not but then it doesn't have a queue). Even if you were to add a queue to a parallel virtual device (e.g., bolt a tbf filter in front of an ipip tunnel device), you would still want to process the queue in sequence to ensure that the packets are ordered correctly. The solution here is to steal a bit from net_device to prevent this. BTW, as qdisc_restart is no longer used by anyone as a module inside the kernel (IIRC it used to with netif_wake_queue), I have not exported the new __qdisc_run function. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/sched/sch_generic.c11
1 files changed, 9 insertions, 2 deletions
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index b1e4c5e20ac7..d7aca8ef524a 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -90,7 +90,7 @@ void qdisc_unlock_tree(struct net_device *dev)
90 NOTE: Called under dev->queue_lock with locally disabled BH. 90 NOTE: Called under dev->queue_lock with locally disabled BH.
91*/ 91*/
92 92
93int qdisc_restart(struct net_device *dev) 93static inline int qdisc_restart(struct net_device *dev)
94{ 94{
95 struct Qdisc *q = dev->qdisc; 95 struct Qdisc *q = dev->qdisc;
96 struct sk_buff *skb; 96 struct sk_buff *skb;
@@ -179,6 +179,14 @@ requeue:
179 return q->q.qlen; 179 return q->q.qlen;
180} 180}
181 181
182void __qdisc_run(struct net_device *dev)
183{
184 while (qdisc_restart(dev) < 0 && !netif_queue_stopped(dev))
185 /* NOTHING */;
186
187 clear_bit(__LINK_STATE_QDISC_RUNNING, &dev->state);
188}
189
182static void dev_watchdog(unsigned long arg) 190static void dev_watchdog(unsigned long arg)
183{ 191{
184 struct net_device *dev = (struct net_device *)arg; 192 struct net_device *dev = (struct net_device *)arg;
@@ -620,6 +628,5 @@ EXPORT_SYMBOL(qdisc_create_dflt);
620EXPORT_SYMBOL(qdisc_alloc); 628EXPORT_SYMBOL(qdisc_alloc);
621EXPORT_SYMBOL(qdisc_destroy); 629EXPORT_SYMBOL(qdisc_destroy);
622EXPORT_SYMBOL(qdisc_reset); 630EXPORT_SYMBOL(qdisc_reset);
623EXPORT_SYMBOL(qdisc_restart);
624EXPORT_SYMBOL(qdisc_lock_tree); 631EXPORT_SYMBOL(qdisc_lock_tree);
625EXPORT_SYMBOL(qdisc_unlock_tree); 632EXPORT_SYMBOL(qdisc_unlock_tree);