sh: convert /proc/cpu/aligmnent, /proc/cpu/kernel_alignment to seq_file
[safe/jmp/linux-2.6] / net / sched / sch_htb.c
index 2f0f0b0..85acab9 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/list.h>
 #include <linux/compiler.h>
 #include <linux/rbtree.h>
+#include <linux/workqueue.h>
 #include <net/netlink.h>
 #include <net/pkt_sched.h>
 
@@ -73,7 +74,7 @@ enum htb_cmode {
 struct htb_class {
        struct Qdisc_class_common common;
        /* general class parameters */
-       struct gnet_stats_basic bstats;
+       struct gnet_stats_basic_packed bstats;
        struct gnet_stats_queue qstats;
        struct gnet_stats_rate_est rate_est;
        struct tc_htb_xstats xstats;    /* our special stats */
@@ -114,8 +115,6 @@ struct htb_class {
        struct tcf_proto *filter_list;
        int filter_cnt;
 
-       int warned;             /* only one warning about non work conserving .. */
-
        /* token bucket parameters */
        struct qdisc_rate_table *rate;  /* rate table of the class itself */
        struct qdisc_rate_table *ceil;  /* ceiling rate (limits borrows too) */
@@ -155,6 +154,10 @@ struct htb_sched {
        int direct_qlen;        /* max qlen of above */
 
        long direct_pkts;
+
+#define HTB_WARN_TOOMANYEVENTS 0x1
+       unsigned int warned;    /* only one warning */
+       struct work_struct work;
 };
 
 /* find class in global hash table using given handle */
@@ -658,7 +661,7 @@ static void htb_charge_class(struct htb_sched *q, struct htb_class *cl,
  * htb_do_events - make mode changes to classes at the level
  *
  * Scans event queue for pending events and applies them. Returns time of
- * next pending event (0 for no event in pq).
+ * next pending event (0 for no event in pq, q->now for too many events).
  * Note: Applied are events whose have cl->pq_key <= q->now.
  */
 static psched_time_t htb_do_events(struct htb_sched *q, int level,
@@ -686,8 +689,14 @@ static psched_time_t htb_do_events(struct htb_sched *q, int level,
                if (cl->cmode != HTB_CAN_SEND)
                        htb_add_to_wait_tree(q, cl, diff);
        }
-       /* too much load - let's continue on next jiffie (including above) */
-       return q->now + 2 * PSCHED_TICKS_PER_SEC / HZ;
+
+       /* too much load - let's continue after a break for scheduling */
+       if (!(q->warned & HTB_WARN_TOOMANYEVENTS)) {
+               printk(KERN_WARNING "htb: too many events!\n");
+               q->warned |= HTB_WARN_TOOMANYEVENTS;
+       }
+
+       return q->now;
 }
 
 /* Returns class->node+prio from id-tree where classe's id is >= id. NULL
@@ -809,13 +818,8 @@ next:
                skb = cl->un.leaf.q->dequeue(cl->un.leaf.q);
                if (likely(skb != NULL))
                        break;
-               if (!cl->warned) {
-                       printk(KERN_WARNING
-                              "htb: class %X isn't work conserving ?!\n",
-                              cl->common.classid);
-                       cl->warned = 1;
-               }
 
+               qdisc_warn_nonwc("htb", cl->un.leaf.q);
                htb_next_rb_node((level ? cl->parent->un.inner.ptr : q->
                                  ptr[0]) + prio);
                cl = htb_lookup_leaf(q->row[level] + prio, prio,
@@ -892,7 +896,10 @@ static struct sk_buff *htb_dequeue(struct Qdisc *sch)
                }
        }
        sch->qstats.overlimits++;
-       qdisc_watchdog_schedule(&q->watchdog, next_event);
+       if (likely(next_event > q->now))
+               qdisc_watchdog_schedule(&q->watchdog, next_event);
+       else
+               schedule_work(&q->work);
 fin:
        return skb;
 }
@@ -962,6 +969,14 @@ static const struct nla_policy htb_policy[TCA_HTB_MAX + 1] = {
        [TCA_HTB_RTAB]  = { .type = NLA_BINARY, .len = TC_RTAB_SIZE },
 };
 
+static void htb_work_func(struct work_struct *work)
+{
+       struct htb_sched *q = container_of(work, struct htb_sched, work);
+       struct Qdisc *sch = q->watchdog.qdisc;
+
+       __netif_schedule(qdisc_root(sch));
+}
+
 static int htb_init(struct Qdisc *sch, struct nlattr *opt)
 {
        struct htb_sched *q = qdisc_priv(sch);
@@ -996,6 +1011,7 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt)
                INIT_LIST_HEAD(q->drops + i);
 
        qdisc_watchdog_init(&q->watchdog, sch);
+       INIT_WORK(&q->work, htb_work_func);
        skb_queue_head_init(&q->direct_queue);
 
        q->direct_qlen = qdisc_dev(sch)->tx_queue_len;
@@ -1101,30 +1117,29 @@ static int htb_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
 {
        struct htb_class *cl = (struct htb_class *)arg;
 
-       if (cl && !cl->level) {
-               if (new == NULL &&
-                   (new = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue,
-                                            &pfifo_qdisc_ops,
-                                            cl->common.classid))
-                   == NULL)
-                       return -ENOBUFS;
-               sch_tree_lock(sch);
-               *old = cl->un.leaf.q;
-               cl->un.leaf.q = new;
-               if (*old != NULL) {
-                       qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
-                       qdisc_reset(*old);
-               }
-               sch_tree_unlock(sch);
-               return 0;
+       if (cl->level)
+               return -EINVAL;
+       if (new == NULL &&
+           (new = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue,
+                                    &pfifo_qdisc_ops,
+                                    cl->common.classid)) == NULL)
+               return -ENOBUFS;
+
+       sch_tree_lock(sch);
+       *old = cl->un.leaf.q;
+       cl->un.leaf.q = new;
+       if (*old != NULL) {
+               qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
+               qdisc_reset(*old);
        }
-       return -ENOENT;
+       sch_tree_unlock(sch);
+       return 0;
 }
 
 static struct Qdisc *htb_leaf(struct Qdisc *sch, unsigned long arg)
 {
        struct htb_class *cl = (struct htb_class *)arg;
-       return (cl && !cl->level) ? cl->un.leaf.q : NULL;
+       return !cl->level ? cl->un.leaf.q : NULL;
 }
 
 static void htb_qlen_notify(struct Qdisc *sch, unsigned long arg)
@@ -1188,7 +1203,6 @@ static void htb_destroy_class(struct Qdisc *sch, struct htb_class *cl)
        kfree(cl);
 }
 
-/* always caled under BH & queue lock */
 static void htb_destroy(struct Qdisc *sch)
 {
        struct htb_sched *q = qdisc_priv(sch);
@@ -1196,6 +1210,7 @@ static void htb_destroy(struct Qdisc *sch)
        struct htb_class *cl;
        unsigned int i;
 
+       cancel_work_sync(&q->work);
        qdisc_watchdog_cancel(&q->watchdog);
        /* This line used to be after htb_destroy_class call below
           and surprisingly it worked in 2.4. But it must precede it
@@ -1259,8 +1274,11 @@ static int htb_delete(struct Qdisc *sch, unsigned long arg)
        if (last_child)
                htb_parent_to_leaf(q, cl, new_q);
 
-       if (--cl->refcnt == 0)
-               htb_destroy_class(sch, cl);
+       BUG_ON(--cl->refcnt == 0);
+       /*
+        * This shouldn't happen: we "hold" one cops->get() when called
+        * from tc_ctl_tclass; the destroy method is done from cops->put().
+        */
 
        sch_tree_unlock(sch);
        return 0;