This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / net / sched / sch_generic.c
index 82117f9..672650c 100644 (file)
@@ -31,7 +31,6 @@
 #include <linux/rtnetlink.h>
 #include <linux/init.h>
 #include <linux/rcupdate.h>
-#include <linux/list.h>
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 
    The idea is the following:
    - enqueue, dequeue are serialized via top level device
      spinlock dev->queue_lock.
-   - tree walking is protected by read_lock_bh(qdisc_tree_lock)
+   - tree walking is protected by read_lock(qdisc_tree_lock)
      and this lock is used only in process context.
-   - updates to tree are made under rtnl semaphore or
-     from softirq context (__qdisc_destroy rcu-callback)
-     hence this lock needs local bh disabling.
+   - updates to tree are made only under rtnl semaphore,
+     hence this lock may be made without local bh disabling.
 
    qdisc_tree_lock must be grabbed BEFORE dev->queue_lock!
  */
 rwlock_t qdisc_tree_lock = RW_LOCK_UNLOCKED;
 
-void qdisc_lock_tree(struct net_device *dev)
-{
-       write_lock_bh(&qdisc_tree_lock);
-       spin_lock_bh(&dev->queue_lock);
-}
-
-void qdisc_unlock_tree(struct net_device *dev)
-{
-       spin_unlock_bh(&dev->queue_lock);
-       write_unlock_bh(&qdisc_tree_lock);
-}
-
 /* 
    dev->queue_lock serializes queue accesses for this device
    AND dev->qdisc pointer itself.
@@ -285,9 +271,10 @@ static const u8 prio2band[TC_PRIO_MAX+1] =
 static int
 pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc* qdisc)
 {
-       struct sk_buff_head *list = qdisc_priv(qdisc);
+       struct sk_buff_head *list;
 
-       list += prio2band[skb->priority&TC_PRIO_MAX];
+       list = ((struct sk_buff_head*)qdisc->data) +
+               prio2band[skb->priority&TC_PRIO_MAX];
 
        if (list->qlen < qdisc->dev->tx_queue_len) {
                __skb_queue_tail(list, skb);
@@ -305,7 +292,7 @@ static struct sk_buff *
 pfifo_fast_dequeue(struct Qdisc* qdisc)
 {
        int prio;
-       struct sk_buff_head *list = qdisc_priv(qdisc);
+       struct sk_buff_head *list = ((struct sk_buff_head*)qdisc->data);
        struct sk_buff *skb;
 
        for (prio = 0; prio < 3; prio++, list++) {
@@ -321,9 +308,10 @@ pfifo_fast_dequeue(struct Qdisc* qdisc)
 static int
 pfifo_fast_requeue(struct sk_buff *skb, struct Qdisc* qdisc)
 {
-       struct sk_buff_head *list = qdisc_priv(qdisc);
+       struct sk_buff_head *list;
 
-       list += prio2band[skb->priority&TC_PRIO_MAX];
+       list = ((struct sk_buff_head*)qdisc->data) +
+               prio2band[skb->priority&TC_PRIO_MAX];
 
        __skb_queue_head(list, skb);
        qdisc->q.qlen++;
@@ -334,7 +322,7 @@ static void
 pfifo_fast_reset(struct Qdisc* qdisc)
 {
        int prio;
-       struct sk_buff_head *list = qdisc_priv(qdisc);
+       struct sk_buff_head *list = ((struct sk_buff_head*)qdisc->data);
 
        for (prio=0; prio < 3; prio++)
                skb_queue_purge(list+prio);
@@ -359,7 +347,9 @@ rtattr_failure:
 static int pfifo_fast_init(struct Qdisc *qdisc, struct rtattr *opt)
 {
        int i;
-       struct sk_buff_head *list = qdisc_priv(qdisc);
+       struct sk_buff_head *list;
+
+       list = ((struct sk_buff_head*)qdisc->data);
 
        for (i=0; i<3; i++)
                skb_queue_head_init(list+i);
@@ -383,30 +373,19 @@ static struct Qdisc_ops pfifo_fast_ops = {
 
 struct Qdisc * qdisc_create_dflt(struct net_device *dev, struct Qdisc_ops *ops)
 {
-       void *p;
        struct Qdisc *sch;
-       int size;
-
-       /* ensure that the Qdisc and the private data are 32-byte aligned */
-       size = ((sizeof(*sch) + QDISC_ALIGN_CONST) & ~QDISC_ALIGN_CONST);
-       size += ops->priv_size + QDISC_ALIGN_CONST;
+       int size = sizeof(*sch) + ops->priv_size;
 
-       p = kmalloc(size, GFP_KERNEL);
-       if (!p)
+       sch = kmalloc(size, GFP_KERNEL);
+       if (!sch)
                return NULL;
-       memset(p, 0, size);
+       memset(sch, 0, size);
 
-       sch = (struct Qdisc *)(((unsigned long)p + QDISC_ALIGN_CONST) 
-                              & ~QDISC_ALIGN_CONST);
-       sch->padded = (char *)sch - (char *)p;
-
-       INIT_LIST_HEAD(&sch->list);
        skb_queue_head_init(&sch->q);
        sch->ops = ops;
        sch->enqueue = ops->enqueue;
        sch->dequeue = ops->dequeue;
        sch->dev = dev;
-       dev_hold(dev);
        sch->stats_lock = &dev->queue_lock;
        atomic_set(&sch->refcnt, 1);
        /* enqueue is accessed locklessly - make sure it's visible
@@ -415,7 +394,7 @@ struct Qdisc * qdisc_create_dflt(struct net_device *dev, struct Qdisc_ops *ops)
        if (!ops->init || ops->init(sch, NULL) == 0)
                return sch;
 
-       kfree(p);
+       kfree(sch);
        return NULL;
 }
 
@@ -440,29 +419,40 @@ static void __qdisc_destroy(struct rcu_head *head)
 #ifdef CONFIG_NET_ESTIMATOR
        qdisc_kill_estimator(&qdisc->stats);
 #endif
-       write_lock(&qdisc_tree_lock);
        if (ops->reset)
                ops->reset(qdisc);
        if (ops->destroy)
                ops->destroy(qdisc);
-       write_unlock(&qdisc_tree_lock);
        module_put(ops->owner);
 
-       dev_put(qdisc->dev);
        if (!(qdisc->flags&TCQ_F_BUILTIN))
-               kfree((char *) qdisc - qdisc->padded);
+               kfree(qdisc);
 }
 
 /* Under dev->queue_lock and BH! */
 
 void qdisc_destroy(struct Qdisc *qdisc)
 {
+       struct net_device *dev = qdisc->dev;
+
        if (!atomic_dec_and_test(&qdisc->refcnt))
                return;
-       list_del(&qdisc->list);
+
+       if (dev) {
+               struct Qdisc *q, **qp;
+               for (qp = &qdisc->dev->qdisc_list; (q=*qp) != NULL; qp = &q->next) {
+                       if (q == qdisc) {
+                               *qp = q->next;
+                               break;
+                       }
+               }
+       }
+
        call_rcu(&qdisc->q_rcu, __qdisc_destroy);
+
 }
 
+
 void dev_activate(struct net_device *dev)
 {
        /* No queueing discipline is attached to device;
@@ -479,15 +469,18 @@ void dev_activate(struct net_device *dev)
                                printk(KERN_INFO "%s: activation failed\n", dev->name);
                                return;
                        }
-                       write_lock_bh(&qdisc_tree_lock);
-                       list_add_tail(&qdisc->list, &dev->qdisc_list);
-                       write_unlock_bh(&qdisc_tree_lock);
+
+                       write_lock(&qdisc_tree_lock);
+                       qdisc->next = dev->qdisc_list;
+                       dev->qdisc_list = qdisc;
+                       write_unlock(&qdisc_tree_lock);
+
                } else {
                        qdisc =  &noqueue_qdisc;
                }
-               write_lock_bh(&qdisc_tree_lock);
+               write_lock(&qdisc_tree_lock);
                dev->qdisc_sleeping = qdisc;
-               write_unlock_bh(&qdisc_tree_lock);
+               write_unlock(&qdisc_tree_lock);
        }
 
        spin_lock_bh(&dev->queue_lock);
@@ -520,11 +513,13 @@ void dev_deactivate(struct net_device *dev)
 
 void dev_init_scheduler(struct net_device *dev)
 {
-       qdisc_lock_tree(dev);
+       write_lock(&qdisc_tree_lock);
+       spin_lock_bh(&dev->queue_lock);
        dev->qdisc = &noop_qdisc;
+       spin_unlock_bh(&dev->queue_lock);
        dev->qdisc_sleeping = &noop_qdisc;
-       INIT_LIST_HEAD(&dev->qdisc_list);
-       qdisc_unlock_tree(dev);
+       dev->qdisc_list = NULL;
+       write_unlock(&qdisc_tree_lock);
 
        dev_watchdog_init(dev);
 }
@@ -533,7 +528,8 @@ void dev_shutdown(struct net_device *dev)
 {
        struct Qdisc *qdisc;
 
-       qdisc_lock_tree(dev);
+       write_lock(&qdisc_tree_lock);
+       spin_lock_bh(&dev->queue_lock);
        qdisc = dev->qdisc_sleeping;
        dev->qdisc = &noop_qdisc;
        dev->qdisc_sleeping = &noop_qdisc;
@@ -544,8 +540,11 @@ void dev_shutdown(struct net_device *dev)
                qdisc_destroy(qdisc);
         }
 #endif
+       BUG_TRAP(dev->qdisc_list == NULL);
        BUG_TRAP(!timer_pending(&dev->watchdog_timer));
-       qdisc_unlock_tree(dev);
+       dev->qdisc_list = NULL;
+       spin_unlock_bh(&dev->queue_lock);
+       write_unlock(&qdisc_tree_lock);
 }
 
 EXPORT_SYMBOL(__netdev_watchdog_up);
@@ -555,5 +554,4 @@ EXPORT_SYMBOL(qdisc_create_dflt);
 EXPORT_SYMBOL(qdisc_destroy);
 EXPORT_SYMBOL(qdisc_reset);
 EXPORT_SYMBOL(qdisc_restart);
-EXPORT_SYMBOL(qdisc_lock_tree);
-EXPORT_SYMBOL(qdisc_unlock_tree);
+EXPORT_SYMBOL(qdisc_tree_lock);