From 750708f31ff99b0a4468b23fd69205df14c1b2b8 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 18 Oct 2007 22:37:58 -0700 Subject: [PATCH] --- yaml --- r: 71532 b: refs/heads/master c: ce0e32e65f70337e0732c97499b643205fa8ea31 h: refs/heads/master v: v3 --- [refs] | 2 +- trunk/net/sched/sch_generic.c | 26 +++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/[refs] b/[refs] index 24d45d627fdc..0c34237b2738 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: a25de534f89c515c82d3553c42d3bb02c2d1a7da +refs/heads/master: ce0e32e65f70337e0732c97499b643205fa8ea31 diff --git a/trunk/net/sched/sch_generic.c b/trunk/net/sched/sch_generic.c index e01d57692c9a..fa1a6f45dc41 100644 --- a/trunk/net/sched/sch_generic.c +++ b/trunk/net/sched/sch_generic.c @@ -556,6 +556,7 @@ void dev_deactivate(struct net_device *dev) { struct Qdisc *qdisc; struct sk_buff *skb; + int running; spin_lock_bh(&dev->queue_lock); qdisc = dev->qdisc; @@ -571,12 +572,31 @@ void dev_deactivate(struct net_device *dev) dev_watchdog_down(dev); - /* Wait for outstanding dev_queue_xmit calls. */ + /* Wait for outstanding qdisc-less dev_queue_xmit calls. */ synchronize_rcu(); /* Wait for outstanding qdisc_run calls. */ - while (test_bit(__LINK_STATE_QDISC_RUNNING, &dev->state)) - yield(); + do { + while (test_bit(__LINK_STATE_QDISC_RUNNING, &dev->state)) + yield(); + + /* + * Double-check inside queue lock to ensure that all effects + * of the queue run are visible when we return. + */ + spin_lock_bh(&dev->queue_lock); + running = test_bit(__LINK_STATE_QDISC_RUNNING, &dev->state); + spin_unlock_bh(&dev->queue_lock); + + /* + * The running flag should never be set at this point because + * we've already set dev->qdisc to noop_qdisc *inside* the same + * pair of spin locks. That is, if any qdisc_run starts after + * our initial test it should see the noop_qdisc and then + * clear the RUNNING bit before dropping the queue lock. So + * if it is set here then we've found a bug. + */ + } while (WARN_ON_ONCE(running)); } void dev_init_scheduler(struct net_device *dev)