pkt_sched: Add and use qdisc_root() and qdisc_root_lock().
When code wants to lock the qdisc tree state, the logic
operation it's doing is locking the top-level qdisc that
sits of the root of the netdev_queue.
Add qdisc_root_lock() to represent this and convert the
easiest cases.
In order for this to work out in all cases, we have to
hook up the noop_qdisc to a dummy netdev_queue.
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index ac208c2..739a871 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -151,14 +151,17 @@
{
int ret = NETDEV_TX_BUSY;
struct net_device *dev;
+ spinlock_t *root_lock;
struct sk_buff *skb;
/* Dequeue packet */
if (unlikely((skb = dequeue_skb(q)) == NULL))
return 0;
- /* And release queue */
- spin_unlock(&txq->lock);
+ root_lock = qdisc_root_lock(q);
+
+ /* And release qdisc */
+ spin_unlock(root_lock);
dev = txq->dev;
@@ -167,7 +170,7 @@
ret = dev_hard_start_xmit(skb, dev, txq);
HARD_TX_UNLOCK(dev, txq);
- spin_lock(&txq->lock);
+ spin_lock(root_lock);
switch (ret) {
case NETDEV_TX_OK:
@@ -345,12 +348,18 @@
.owner = THIS_MODULE,
};
+static struct netdev_queue noop_netdev_queue = {
+ .lock = __SPIN_LOCK_UNLOCKED(noop_netdev_queue.lock),
+ .qdisc = &noop_qdisc,
+};
+
struct Qdisc noop_qdisc = {
.enqueue = noop_enqueue,
.dequeue = noop_dequeue,
.flags = TCQ_F_BUILTIN,
.ops = &noop_qdisc_ops,
.list = LIST_HEAD_INIT(noop_qdisc.list),
+ .dev_queue = &noop_netdev_queue,
};
EXPORT_SYMBOL(noop_qdisc);
@@ -666,19 +675,21 @@
for (i = 0; i < dev->num_tx_queues; i++) {
struct netdev_queue *dev_queue;
+ spinlock_t *root_lock;
struct Qdisc *q;
int val;
dev_queue = netdev_get_tx_queue(dev, i);
q = dev_queue->qdisc;
+ root_lock = qdisc_root_lock(q);
if (lock)
- spin_lock_bh(&dev_queue->lock);
+ spin_lock_bh(root_lock);
val = test_bit(__QDISC_STATE_RUNNING, &q->state);
if (lock)
- spin_unlock_bh(&dev_queue->lock);
+ spin_unlock_bh(root_lock);
if (val)
return true;