]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - net/sched/sch_htb.c
[NET_SCHED]: Use nla_nest_start/nla_nest_end
[linux-2.6.git] / net / sched / sch_htb.c
index 6c6cac65255f703abf6b15d439dd10a3b7d83eb0..69fac320f8bcdb1eeeabb946bdc44ae41716621e 100644 (file)
@@ -11,7 +11,7 @@
  * Credits (in time order) for older HTB versions:
  *              Stef Coene <stef.coene@docum.org>
  *                     HTB support at LARTC mailing list
- *             Ondrej Kraus, <krauso@barr.cz> 
+ *             Ondrej Kraus, <krauso@barr.cz>
  *                     found missing INIT_QDISC(htb)
  *             Vladimir Smelhaus, Aamer Akhter, Bert Hubert
  *                     helped a lot to locate nasty class stall bug
  * $Id: sch_htb.c,v 1.25 2003/12/07 11:08:25 devik Exp devik $
  */
 #include <linux/module.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/bitops.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
-#include <linux/sched.h>
 #include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/in.h>
 #include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/if_ether.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/notifier.h>
-#include <net/ip.h>
-#include <net/route.h>
 #include <linux/skbuff.h>
 #include <linux/list.h>
 #include <linux/compiler.h>
-#include <net/sock.h>
-#include <net/pkt_sched.h>
 #include <linux/rbtree.h>
+#include <net/netlink.h>
+#include <net/pkt_sched.h>
 
 /* HTB algorithm.
     Author: devik@cdi.cz
     ========================================================================
     HTB is like TBF with multiple classes. It is also similar to CBQ because
-    it allows to assign priority to each class in hierarchy. 
+    it allows to assign priority to each class in hierarchy.
     In fact it is another implementation of Floyd's formal sharing.
 
     Levels:
-    Each class is assigned level. Leaf has ALWAYS level 0 and root 
+    Each class is assigned level. Leaf has ALWAYS level 0 and root
     classes have level TC_HTB_MAXDEPTH-1. Interior nodes has level
     one less than their parent.
 */
 
 #define HTB_HSIZE 16           /* classid hash size */
-#define HTB_EWMAC 2            /* rate average over HTB_EWMAC*HTB_HSIZE sec */
-#define HTB_RATECM 1           /* whether to use rate computer */
 #define HTB_HYSTERESIS 1       /* whether to use mode hysteresis for speedup */
 #define HTB_VER 0x30011                /* major must be matched with number suplied by TC as version */
 
@@ -95,16 +77,10 @@ struct htb_class {
        struct tc_htb_xstats xstats;    /* our special stats */
        int refcnt;             /* usage count of this class */
 
-#ifdef HTB_RATECM
-       /* rate measurement counters */
-       unsigned long rate_bytes, sum_bytes;
-       unsigned long rate_packets, sum_packets;
-#endif
-
        /* topology */
        int level;              /* our level (see above) */
        struct htb_class *parent;       /* parent class */
-       struct list_head hlist; /* classid hash list item */
+       struct hlist_node hlist;        /* classid hash list item */
        struct list_head sibling;       /* sibling list item */
        struct list_head children;      /* children list */
 
@@ -129,7 +105,7 @@ struct htb_class {
        } un;
        struct rb_node node[TC_HTB_NUMPRIO];    /* node for self or feed tree */
        struct rb_node pq_node; /* node for event queue */
-       unsigned long pq_key;   /* the same type as jiffies global */
+       psched_time_t pq_key;
 
        int prio_activity;      /* for which prios are we active */
        enum htb_cmode cmode;   /* current mode of the class */
@@ -147,24 +123,23 @@ struct htb_class {
        psched_tdiff_t mbuffer; /* max wait time */
        long tokens, ctokens;   /* current number of tokens */
        psched_time_t t_c;      /* checkpoint time */
+
+       int prio;               /* For parent to leaf return possible here */
+       int quantum;            /* we do backup. Finally full replacement  */
+                               /* of un.leaf originals should be done. */
 };
 
-/* TODO: maybe compute rate when size is too large .. or drop ? */
 static inline long L2T(struct htb_class *cl, struct qdisc_rate_table *rate,
                           int size)
 {
-       int slot = size >> rate->rate.cell_log;
-       if (slot > 255) {
-               cl->xstats.giants++;
-               slot = 255;
-       }
-       return rate->data[slot];
+       long result = qdisc_l2t(rate, size);
+       return result;
 }
 
 struct htb_sched {
        struct list_head root;  /* root classes list */
-       struct list_head hash[HTB_HSIZE];       /* hashed by classid */
-       struct list_head drops[TC_HTB_NUMPRIO]; /* active leaves (for drops) */
+       struct hlist_head hash[HTB_HSIZE];      /* hashed by classid */
+       struct list_head drops[TC_HTB_NUMPRIO];/* active leaves (for drops) */
 
        /* self list - roots of self generating tree */
        struct rb_root row[TC_HTB_MAXDEPTH][TC_HTB_NUMPRIO];
@@ -176,10 +151,7 @@ struct htb_sched {
        struct rb_root wait_pq[TC_HTB_MAXDEPTH];
 
        /* time of nearest event per level (row) */
-       unsigned long near_ev_cache[TC_HTB_MAXDEPTH];
-
-       /* cached value of jiffies in dequeue */
-       unsigned long jiffies;
+       psched_time_t near_ev_cache[TC_HTB_MAXDEPTH];
 
        /* whether we hit non-work conserving class during this dequeue; we use */
        int nwc_hit;            /* this to disable mindelay complaint in dequeue */
@@ -192,11 +164,7 @@ struct htb_sched {
 
        int rate2quantum;       /* quant = rate / rate2quantum */
        psched_time_t now;      /* cached dequeue time */
-       struct timer_list timer;        /* send delay timer */
-#ifdef HTB_RATECM
-       struct timer_list rttim;        /* rate computer timer */
-       int recmp_bucket;       /* which hash bucket to recompute next */
-#endif
+       struct qdisc_watchdog watchdog;
 
        /* non shaped skbs; let them go directly thru */
        struct sk_buff_head direct_queue;
@@ -220,12 +188,13 @@ static inline int htb_hash(u32 h)
 static inline struct htb_class *htb_find(u32 handle, struct Qdisc *sch)
 {
        struct htb_sched *q = qdisc_priv(sch);
-       struct list_head *p;
+       struct hlist_node *p;
+       struct htb_class *cl;
+
        if (TC_H_MAJ(handle) != sch->handle)
                return NULL;
 
-       list_for_each(p, q->hash + htb_hash(handle)) {
-               struct htb_class *cl = list_entry(p, struct htb_class, hlist);
+       hlist_for_each_entry(cl, p, q->hash + htb_hash(handle), hlist) {
                if (cl->classid == handle)
                        return cl;
        }
@@ -240,15 +209,11 @@ static inline struct htb_class *htb_find(u32 handle, struct Qdisc *sch)
  * We allow direct class selection by classid in priority. The we examine
  * filters in qdisc and in inner nodes (if higher filter points to the inner
  * node). If we end up with classid MAJOR:0 we enqueue the skb into special
- * internal fifo (direct). These packets then go directly thru. If we still 
+ * internal fifo (direct). These packets then go directly thru. If we still
  * have no valid leaf we try to use MAJOR:default leaf. It still unsuccessfull
  * then finish and return direct queue.
  */
 #define HTB_DIRECT (struct htb_class*)-1
-static inline u32 htb_classid(struct htb_class *cl)
-{
-       return (cl && cl != HTB_DIRECT) ? cl->classid : TC_H_UNSPEC;
-}
 
 static struct htb_class *htb_classify(struct sk_buff *skb, struct Qdisc *sch,
                                      int *qerr)
@@ -278,9 +243,6 @@ static struct htb_class *htb_classify(struct sk_buff *skb, struct Qdisc *sch,
                case TC_ACT_SHOT:
                        return NULL;
                }
-#elif defined(CONFIG_NET_CLS_POLICE)
-               if (result == TC_POLICE_SHOT)
-                       return HTB_DIRECT;
 #endif
                if ((cl = (void *)res.class) == NULL) {
                        if (res.classid == sch->handle)
@@ -338,19 +300,19 @@ static void htb_add_to_wait_tree(struct htb_sched *q,
 {
        struct rb_node **p = &q->wait_pq[cl->level].rb_node, *parent = NULL;
 
-       cl->pq_key = q->jiffies + PSCHED_US2JIFFIE(delay);
-       if (cl->pq_key == q->jiffies)
+       cl->pq_key = q->now + delay;
+       if (cl->pq_key == q->now)
                cl->pq_key++;
 
        /* update the nearest event cache */
-       if (time_after(q->near_ev_cache[cl->level], cl->pq_key))
+       if (q->near_ev_cache[cl->level] > cl->pq_key)
                q->near_ev_cache[cl->level] = cl->pq_key;
 
        while (*p) {
                struct htb_class *c;
                parent = *p;
                c = rb_entry(parent, struct htb_class, pq_node);
-               if (time_after_eq(cl->pq_key, c->pq_key))
+               if (cl->pq_key >= c->pq_key)
                        p = &parent->rb_right;
                else
                        p = &parent->rb_left;
@@ -365,7 +327,7 @@ static void htb_add_to_wait_tree(struct htb_sched *q,
  * When we are past last key we return NULL.
  * Average complexity is 2 steps per call.
  */
-static void htb_next_rb_node(struct rb_node **n)
+static inline void htb_next_rb_node(struct rb_node **n)
 {
        *n = rb_next(*n);
 }
@@ -387,6 +349,18 @@ static inline void htb_add_class_to_row(struct htb_sched *q,
        }
 }
 
+/* If this triggers, it is a bug in this code, but it need not be fatal */
+static void htb_safe_rb_erase(struct rb_node *rb, struct rb_root *root)
+{
+       if (RB_EMPTY_NODE(rb)) {
+               WARN_ON(1);
+       } else {
+               rb_erase(rb, root);
+               RB_CLEAR_NODE(rb);
+       }
+}
+
+
 /**
  * htb_remove_class_from_row - removes class from its row
  *
@@ -400,10 +374,12 @@ static inline void htb_remove_class_from_row(struct htb_sched *q,
 
        while (mask) {
                int prio = ffz(~mask);
+
                mask &= ~(1 << prio);
                if (q->ptr[cl->level][prio] == cl->node + prio)
                        htb_next_rb_node(q->ptr[cl->level] + prio);
-               rb_erase(cl->node + prio, q->row[cl->level] + prio);
+
+               htb_safe_rb_erase(cl->node + prio, q->row[cl->level] + prio);
                if (!q->row[cl->level][prio].rb_node)
                        m |= 1 << prio;
        }
@@ -414,7 +390,7 @@ static inline void htb_remove_class_from_row(struct htb_sched *q,
  * htb_activate_prios - creates active classe's feed chain
  *
  * The class is connected to ancestors and/or appropriate rows
- * for priorities it is participating on. cl->cmode must be new 
+ * for priorities it is participating on. cl->cmode must be new
  * (activated) mode. It does nothing if cl->prio_activity == 0.
  */
 static void htb_activate_prios(struct htb_sched *q, struct htb_class *cl)
@@ -447,7 +423,7 @@ static void htb_activate_prios(struct htb_sched *q, struct htb_class *cl)
 /**
  * htb_deactivate_prios - remove class from feed chain
  *
- * cl->cmode must represent old mode (before deactivation). It does 
+ * cl->cmode must represent old mode (before deactivation). It does
  * nothing if cl->prio_activity == 0. Class is removed from all feed
  * chains and rows.
  */
@@ -471,7 +447,7 @@ static void htb_deactivate_prios(struct htb_sched *q, struct htb_class *cl)
                                p->un.inner.ptr[prio] = NULL;
                        }
 
-                       rb_erase(cl->node + prio, p->un.inner.feed + prio);
+                       htb_safe_rb_erase(cl->node + prio, p->un.inner.feed + prio);
 
                        if (!p->un.inner.feed[prio].rb_node)
                                mask |= 1 << prio;
@@ -505,9 +481,9 @@ static inline long htb_hiwater(const struct htb_class *cl)
  *
  * It computes cl's mode at time cl->t_c+diff and returns it. If mode
  * is not HTB_CAN_SEND then cl->pq_key is updated to time difference
- * from now to time when cl will change its state. 
+ * from now to time when cl will change its state.
  * Also it is worth to note that class mode doesn't change simply
- * at cl->{c,}tokens == 0 but there can rather be hysteresis of 
+ * at cl->{c,}tokens == 0 but there can rather be hysteresis of
  * 0 .. -cl->{c,}buffer range. It is meant to limit number of
  * mode transitions per time unit. The speed gain is about 1/6.
  */
@@ -556,7 +532,7 @@ htb_change_class_mode(struct htb_sched *q, struct htb_class *cl, long *diff)
 }
 
 /**
- * htb_activate - inserts leaf cl into appropriate active feeds 
+ * htb_activate - inserts leaf cl into appropriate active feeds
  *
  * Routine learns (new) priority of leaf and activates feed chain
  * for the prio. It can be called on already active leaf safely.
@@ -575,7 +551,7 @@ static inline void htb_activate(struct htb_sched *q, struct htb_class *cl)
 }
 
 /**
- * htb_deactivate - remove leaf cl from active feeds 
+ * htb_deactivate - remove leaf cl from active feeds
  *
  * Make sure that leaf is active. In the other words it can't be called
  * with non-active leaf. It also removes class from the drop list.
@@ -618,13 +594,14 @@ static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch)
                cl->qstats.drops++;
                return NET_XMIT_DROP;
        } else {
-               cl->bstats.packets++;
+               cl->bstats.packets +=
+                       skb_is_gso(skb)?skb_shinfo(skb)->gso_segs:1;
                cl->bstats.bytes += skb->len;
                htb_activate(q, cl);
        }
 
        sch->q.qlen++;
-       sch->bstats.packets++;
+       sch->bstats.packets += skb_is_gso(skb)?skb_shinfo(skb)->gso_segs:1;
        sch->bstats.bytes += skb->len;
        return NET_XMIT_SUCCESS;
 }
@@ -661,41 +638,6 @@ static int htb_requeue(struct sk_buff *skb, struct Qdisc *sch)
        return NET_XMIT_SUCCESS;
 }
 
-static void htb_timer(unsigned long arg)
-{
-       struct Qdisc *sch = (struct Qdisc *)arg;
-       sch->flags &= ~TCQ_F_THROTTLED;
-       wmb();
-       netif_schedule(sch->dev);
-}
-
-#ifdef HTB_RATECM
-#define RT_GEN(D,R) R+=D-(R/HTB_EWMAC);D=0
-static void htb_rate_timer(unsigned long arg)
-{
-       struct Qdisc *sch = (struct Qdisc *)arg;
-       struct htb_sched *q = qdisc_priv(sch);
-       struct list_head *p;
-
-       /* lock queue so that we can muck with it */
-       spin_lock_bh(&sch->dev->queue_lock);
-
-       q->rttim.expires = jiffies + HZ;
-       add_timer(&q->rttim);
-
-       /* scan and recompute one bucket at time */
-       if (++q->recmp_bucket >= HTB_HSIZE)
-               q->recmp_bucket = 0;
-       list_for_each(p, q->hash + q->recmp_bucket) {
-               struct htb_class *cl = list_entry(p, struct htb_class, hlist);
-
-               RT_GEN(cl->sum_bytes, cl->rate_bytes);
-               RT_GEN(cl->sum_packets, cl->rate_packets);
-       }
-       spin_unlock_bh(&sch->dev->queue_lock);
-}
-#endif
-
 /**
  * htb_charge_class - charges amount "bytes" to leaf and ancestors
  *
@@ -708,8 +650,9 @@ static void htb_rate_timer(unsigned long arg)
  * In such case we remove class from event queue first.
  */
 static void htb_charge_class(struct htb_sched *q, struct htb_class *cl,
-                            int level, int bytes)
+                            int level, struct sk_buff *skb)
 {
+       int bytes = skb->len;
        long toks, diff;
        enum htb_cmode old_mode;
 
@@ -720,7 +663,7 @@ static void htb_charge_class(struct htb_sched *q, struct htb_class *cl,
        cl->T = toks
 
        while (cl) {
-               diff = PSCHED_TDIFF_SAFE(q->now, cl->t_c, (u32) cl->mbuffer);
+               diff = psched_tdiff_bounded(q->now, cl->t_c, cl->mbuffer);
                if (cl->level >= level) {
                        if (cl->level == level)
                                cl->xstats.lends++;
@@ -737,20 +680,16 @@ static void htb_charge_class(struct htb_sched *q, struct htb_class *cl,
                htb_change_class_mode(q, cl, &diff);
                if (old_mode != cl->cmode) {
                        if (old_mode != HTB_CAN_SEND)
-                               rb_erase(&cl->pq_node, q->wait_pq + cl->level);
+                               htb_safe_rb_erase(&cl->pq_node, q->wait_pq + cl->level);
                        if (cl->cmode != HTB_CAN_SEND)
                                htb_add_to_wait_tree(q, cl, diff);
                }
-#ifdef HTB_RATECM
-               /* update rate counters */
-               cl->sum_bytes += bytes;
-               cl->sum_packets++;
-#endif
 
                /* update byte stats except for leaves which are already updated */
                if (cl->level) {
                        cl->bstats.bytes += bytes;
-                       cl->bstats.packets++;
+                       cl->bstats.packets += skb_is_gso(skb)?
+                                       skb_shinfo(skb)->gso_segs:1;
                }
                cl = cl->parent;
        }
@@ -759,36 +698,35 @@ 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 jiffies to
+ * Scans event queue for pending events and applies them. Returns time of
  * next pending event (0 for no event in pq).
- * Note: Aplied are events whose have cl->pq_key <= jiffies.
+ * Note: Applied are events whose have cl->pq_key <= q->now.
  */
-static long htb_do_events(struct htb_sched *q, int level)
+static psched_time_t htb_do_events(struct htb_sched *q, int level)
 {
        int i;
 
        for (i = 0; i < 500; i++) {
                struct htb_class *cl;
                long diff;
-               struct rb_node *p = q->wait_pq[level].rb_node;
+               struct rb_node *p = rb_first(&q->wait_pq[level]);
+
                if (!p)
                        return 0;
-               while (p->rb_left)
-                       p = p->rb_left;
 
                cl = rb_entry(p, struct htb_class, pq_node);
-               if (time_after(cl->pq_key, q->jiffies)) {
-                       return cl->pq_key - q->jiffies;
-               }
-               rb_erase(p, q->wait_pq + level);
-               diff = PSCHED_TDIFF_SAFE(q->now, cl->t_c, (u32) cl->mbuffer);
+               if (cl->pq_key > q->now)
+                       return cl->pq_key;
+
+               htb_safe_rb_erase(p, q->wait_pq + level);
+               diff = psched_tdiff_bounded(q->now, cl->t_c, cl->mbuffer);
                htb_change_class_mode(q, cl, &diff);
                if (cl->cmode != HTB_CAN_SEND)
                        htb_add_to_wait_tree(q, cl, diff);
        }
        if (net_ratelimit())
                printk(KERN_WARNING "htb: too many events !\n");
-       return HZ / 10;
+       return q->now + PSCHED_TICKS_PER_SEC / 10;
 }
 
 /* Returns class->node+prio from id-tree where classe's id is >= id. NULL
@@ -835,7 +773,7 @@ static struct htb_class *htb_lookup_leaf(struct rb_root *tree, int prio,
 
        for (i = 0; i < 65535; i++) {
                if (!*sp->pptr && *sp->pid) {
-                       /* ptr was invalidated but id is valid - try to recover 
+                       /* ptr was invalidated but id is valid - try to recover
                           the original or next ptr */
                        *sp->pptr =
                            htb_id_find_next_upper(prio, sp->root, *sp->pid);
@@ -887,7 +825,7 @@ next:
 
                /* class can be empty - it is unlikely but can be true if leaf
                   qdisc drops packets in enqueue routine or if someone used
-                  graft operation on the leaf since last dequeue; 
+                  graft operation on the leaf since last dequeue;
                   simply deactivate and skip such class */
                if (unlikely(cl->un.leaf.q->q.qlen == 0)) {
                        struct htb_class *next;
@@ -935,35 +873,17 @@ next:
                   gives us slightly better performance */
                if (!cl->un.leaf.q->q.qlen)
                        htb_deactivate(q, cl);
-               htb_charge_class(q, cl, level, skb->len);
+               htb_charge_class(q, cl, level, skb);
        }
        return skb;
 }
 
-static void htb_delay_by(struct Qdisc *sch, long delay)
-{
-       struct htb_sched *q = qdisc_priv(sch);
-       if (delay <= 0)
-               delay = 1;
-       if (unlikely(delay > 5 * HZ)) {
-               if (net_ratelimit())
-                       printk(KERN_INFO "HTB delay %ld > 5sec\n", delay);
-               delay = 5 * HZ;
-       }
-       /* why don't use jiffies here ? because expires can be in past */
-       mod_timer(&q->timer, q->jiffies + delay);
-       sch->flags |= TCQ_F_THROTTLED;
-       sch->qstats.overlimits++;
-}
-
 static struct sk_buff *htb_dequeue(struct Qdisc *sch)
 {
        struct sk_buff *skb = NULL;
        struct htb_sched *q = qdisc_priv(sch);
        int level;
-       long min_delay;
-
-       q->jiffies = jiffies;
+       psched_time_t next_event;
 
        /* try to dequeue direct packets as high prio (!) to minimize cpu work */
        skb = __skb_dequeue(&q->direct_queue);
@@ -975,23 +895,26 @@ static struct sk_buff *htb_dequeue(struct Qdisc *sch)
 
        if (!sch->q.qlen)
                goto fin;
-       PSCHED_GET_TIME(q->now);
+       q->now = psched_get_time();
 
-       min_delay = LONG_MAX;
+       next_event = q->now + 5 * PSCHED_TICKS_PER_SEC;
        q->nwc_hit = 0;
        for (level = 0; level < TC_HTB_MAXDEPTH; level++) {
                /* common case optimization - skip event handler quickly */
                int m;
-               long delay;
-               if (time_after_eq(q->jiffies, q->near_ev_cache[level])) {
-                       delay = htb_do_events(q, level);
-                       q->near_ev_cache[level] =
-                           q->jiffies + (delay ? delay : HZ);
+               psched_time_t event;
+
+               if (q->now >= q->near_ev_cache[level]) {
+                       event = htb_do_events(q, level);
+                       if (!event)
+                               event = q->now + PSCHED_TICKS_PER_SEC;
+                       q->near_ev_cache[level] = event;
                } else
-                       delay = q->near_ev_cache[level] - q->jiffies;
+                       event = q->near_ev_cache[level];
+
+               if (event && next_event > event)
+                       next_event = event;
 
-               if (delay && min_delay > delay)
-                       min_delay = delay;
                m = ~q->row_mask[level];
                while (m != (int)(-1)) {
                        int prio = ffz(m);
@@ -1004,7 +927,8 @@ static struct sk_buff *htb_dequeue(struct Qdisc *sch)
                        }
                }
        }
-       htb_delay_by(sch, min_delay > 5 * HZ ? 5 * HZ : min_delay);
+       sch->qstats.overlimits++;
+       qdisc_watchdog_schedule(&q->watchdog, next_event);
 fin:
        return skb;
 }
@@ -1041,10 +965,10 @@ static void htb_reset(struct Qdisc *sch)
        int i;
 
        for (i = 0; i < HTB_HSIZE; i++) {
-               struct list_head *p;
-               list_for_each(p, q->hash + i) {
-                       struct htb_class *cl =
-                           list_entry(p, struct htb_class, hlist);
+               struct hlist_node *p;
+               struct htb_class *cl;
+
+               hlist_for_each_entry(cl, p, q->hash + i, hlist) {
                        if (cl->level)
                                memset(&cl->un.inner, 0, sizeof(cl->un.inner));
                        else {
@@ -1057,8 +981,7 @@ static void htb_reset(struct Qdisc *sch)
 
                }
        }
-       sch->flags &= ~TCQ_F_THROTTLED;
-       del_timer(&q->timer);
+       qdisc_watchdog_cancel(&q->watchdog);
        __skb_queue_purge(&q->direct_queue);
        sch->q.qlen = 0;
        memset(q->row, 0, sizeof(q->row));
@@ -1069,19 +992,27 @@ static void htb_reset(struct Qdisc *sch)
                INIT_LIST_HEAD(q->drops + i);
 }
 
-static int htb_init(struct Qdisc *sch, struct rtattr *opt)
+static int htb_init(struct Qdisc *sch, struct nlattr *opt)
 {
        struct htb_sched *q = qdisc_priv(sch);
-       struct rtattr *tb[TCA_HTB_INIT];
+       struct nlattr *tb[TCA_HTB_INIT + 1];
        struct tc_htb_glob *gopt;
+       int err;
        int i;
-       if (!opt || rtattr_parse_nested(tb, TCA_HTB_INIT, opt) ||
-           tb[TCA_HTB_INIT - 1] == NULL ||
-           RTA_PAYLOAD(tb[TCA_HTB_INIT - 1]) < sizeof(*gopt)) {
+
+       if (!opt)
+               return -EINVAL;
+
+       err = nla_parse_nested(tb, TCA_HTB_INIT, opt, NULL);
+       if (err < 0)
+               return err;
+
+       if (tb[TCA_HTB_INIT] == NULL ||
+           nla_len(tb[TCA_HTB_INIT]) < sizeof(*gopt)) {
                printk(KERN_ERR "HTB: hey probably you have bad tc tool ?\n");
                return -EINVAL;
        }
-       gopt = RTA_DATA(tb[TCA_HTB_INIT - 1]);
+       gopt = nla_data(tb[TCA_HTB_INIT]);
        if (gopt->version != HTB_VER >> 16) {
                printk(KERN_ERR
                       "HTB: need tc/htb version %d (minor is %d), you have %d\n",
@@ -1091,26 +1022,17 @@ static int htb_init(struct Qdisc *sch, struct rtattr *opt)
 
        INIT_LIST_HEAD(&q->root);
        for (i = 0; i < HTB_HSIZE; i++)
-               INIT_LIST_HEAD(q->hash + i);
+               INIT_HLIST_HEAD(q->hash + i);
        for (i = 0; i < TC_HTB_NUMPRIO; i++)
                INIT_LIST_HEAD(q->drops + i);
 
-       init_timer(&q->timer);
+       qdisc_watchdog_init(&q->watchdog, sch);
        skb_queue_head_init(&q->direct_queue);
 
        q->direct_qlen = sch->dev->tx_queue_len;
        if (q->direct_qlen < 2) /* some devices have zero tx_queue_len */
                q->direct_qlen = 2;
-       q->timer.function = htb_timer;
-       q->timer.data = (unsigned long)sch;
-
-#ifdef HTB_RATECM
-       init_timer(&q->rttim);
-       q->rttim.function = htb_rate_timer;
-       q->rttim.data = (unsigned long)sch;
-       q->rttim.expires = jiffies + HZ;
-       add_timer(&q->rttim);
-#endif
+
        if ((q->rate2quantum = gopt->rate2quantum) < 1)
                q->rate2quantum = 1;
        q->defcls = gopt->defcls;
@@ -1121,25 +1043,29 @@ static int htb_init(struct Qdisc *sch, struct rtattr *opt)
 static int htb_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
        struct htb_sched *q = qdisc_priv(sch);
-       unsigned char *b = skb->tail;
-       struct rtattr *rta;
+       struct nlattr *nest;
        struct tc_htb_glob gopt;
+
        spin_lock_bh(&sch->dev->queue_lock);
-       gopt.direct_pkts = q->direct_pkts;
 
+       gopt.direct_pkts = q->direct_pkts;
        gopt.version = HTB_VER;
        gopt.rate2quantum = q->rate2quantum;
        gopt.defcls = q->defcls;
        gopt.debug = 0;
-       rta = (struct rtattr *)b;
-       RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
-       RTA_PUT(skb, TCA_HTB_INIT, sizeof(gopt), &gopt);
-       rta->rta_len = skb->tail - b;
+
+       nest = nla_nest_start(skb, TCA_OPTIONS);
+       if (nest == NULL)
+               goto nla_put_failure;
+       NLA_PUT(skb, TCA_HTB_INIT, sizeof(gopt), &gopt);
+       nla_nest_end(skb, nest);
+
        spin_unlock_bh(&sch->dev->queue_lock);
        return skb->len;
-rtattr_failure:
+
+nla_put_failure:
        spin_unlock_bh(&sch->dev->queue_lock);
-       skb_trim(skb, skb->tail - skb->data);
+       nla_nest_cancel(skb, nest);
        return -1;
 }
 
@@ -1147,8 +1073,7 @@ static int htb_dump_class(struct Qdisc *sch, unsigned long arg,
                          struct sk_buff *skb, struct tcmsg *tcm)
 {
        struct htb_class *cl = (struct htb_class *)arg;
-       unsigned char *b = skb->tail;
-       struct rtattr *rta;
+       struct nlattr *nest;
        struct tc_htb_opt opt;
 
        spin_lock_bh(&sch->dev->queue_lock);
@@ -1157,8 +1082,9 @@ static int htb_dump_class(struct Qdisc *sch, unsigned long arg,
        if (!cl->level && cl->un.leaf.q)
                tcm->tcm_info = cl->un.leaf.q->handle;
 
-       rta = (struct rtattr *)b;
-       RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
+       nest = nla_nest_start(skb, TCA_OPTIONS);
+       if (nest == NULL)
+               goto nla_put_failure;
 
        memset(&opt, 0, sizeof(opt));
 
@@ -1169,13 +1095,15 @@ static int htb_dump_class(struct Qdisc *sch, unsigned long arg,
        opt.quantum = cl->un.leaf.quantum;
        opt.prio = cl->un.leaf.prio;
        opt.level = cl->level;
-       RTA_PUT(skb, TCA_HTB_PARMS, sizeof(opt), &opt);
-       rta->rta_len = skb->tail - b;
+       NLA_PUT(skb, TCA_HTB_PARMS, sizeof(opt), &opt);
+
+       nla_nest_end(skb, nest);
        spin_unlock_bh(&sch->dev->queue_lock);
        return skb->len;
-rtattr_failure:
+
+nla_put_failure:
        spin_unlock_bh(&sch->dev->queue_lock);
-       skb_trim(skb, b - skb->data);
+       nla_nest_cancel(skb, nest);
        return -1;
 }
 
@@ -1184,11 +1112,6 @@ htb_dump_class_stats(struct Qdisc *sch, unsigned long arg, struct gnet_dump *d)
 {
        struct htb_class *cl = (struct htb_class *)arg;
 
-#ifdef HTB_RATECM
-       cl->rate_est.bps = cl->rate_bytes / (HTB_EWMAC * HTB_HSIZE);
-       cl->rate_est.pps = cl->rate_packets / (HTB_EWMAC * HTB_HSIZE);
-#endif
-
        if (!cl->level && cl->un.leaf.q)
                cl->qstats.qlen = cl->un.leaf.q->q.qlen;
        cl->xstats.tokens = cl->tokens;
@@ -1208,17 +1131,14 @@ 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(sch->dev,
-                                                           &pfifo_qdisc_ops))
+               if (new == NULL &&
+                   (new = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops,
+                                            cl->classid))
                    == NULL)
                        return -ENOBUFS;
                sch_tree_lock(sch);
                if ((*old = xchg(&cl->un.leaf.q, new)) != NULL) {
-                       if (cl->prio_activity)
-                               htb_deactivate(qdisc_priv(sch), cl);
-
-                       /* TODO: is it correct ? Why CBQ doesn't do it ? */
-                       sch->q.qlen -= (*old)->q.qlen;
+                       qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
                        qdisc_reset(*old);
                }
                sch_tree_unlock(sch);
@@ -1233,6 +1153,14 @@ static struct Qdisc *htb_leaf(struct Qdisc *sch, unsigned long arg)
        return (cl && !cl->level) ? cl->un.leaf.q : NULL;
 }
 
+static void htb_qlen_notify(struct Qdisc *sch, unsigned long arg)
+{
+       struct htb_class *cl = (struct htb_class *)arg;
+
+       if (cl->un.leaf.q->q.qlen == 0)
+               htb_deactivate(qdisc_priv(sch), cl);
+}
+
 static unsigned long htb_get(struct Qdisc *sch, u32 classid)
 {
        struct htb_class *cl = htb_find(classid, sch);
@@ -1241,42 +1169,65 @@ static unsigned long htb_get(struct Qdisc *sch, u32 classid)
        return (unsigned long)cl;
 }
 
-static void htb_destroy_filters(struct tcf_proto **fl)
+static inline int htb_parent_last_child(struct htb_class *cl)
 {
-       struct tcf_proto *tp;
+       if (!cl->parent)
+               /* the root class */
+               return 0;
 
-       while ((tp = *fl) != NULL) {
-               *fl = tp->next;
-               tcf_destroy(tp);
-       }
+       if (!(cl->parent->children.next == &cl->sibling &&
+               cl->parent->children.prev == &cl->sibling))
+               /* not the last child */
+               return 0;
+
+       return 1;
+}
+
+static void htb_parent_to_leaf(struct htb_class *cl, struct Qdisc *new_q)
+{
+       struct htb_class *parent = cl->parent;
+
+       BUG_TRAP(!cl->level && cl->un.leaf.q && !cl->prio_activity);
+
+       parent->level = 0;
+       memset(&parent->un.inner, 0, sizeof(parent->un.inner));
+       INIT_LIST_HEAD(&parent->un.leaf.drop_list);
+       parent->un.leaf.q = new_q ? new_q : &noop_qdisc;
+       parent->un.leaf.quantum = parent->quantum;
+       parent->un.leaf.prio = parent->prio;
+       parent->tokens = parent->buffer;
+       parent->ctokens = parent->cbuffer;
+       parent->t_c = psched_get_time();
+       parent->cmode = HTB_CAN_SEND;
 }
 
 static void htb_destroy_class(struct Qdisc *sch, struct htb_class *cl)
 {
        struct htb_sched *q = qdisc_priv(sch);
+
        if (!cl->level) {
                BUG_TRAP(cl->un.leaf.q);
-               sch->q.qlen -= cl->un.leaf.q->q.qlen;
                qdisc_destroy(cl->un.leaf.q);
        }
+       gen_kill_estimator(&cl->bstats, &cl->rate_est);
        qdisc_put_rtab(cl->rate);
        qdisc_put_rtab(cl->ceil);
 
-       htb_destroy_filters(&cl->filter_list);
+       tcf_destroy_chain(cl->filter_list);
 
        while (!list_empty(&cl->children))
                htb_destroy_class(sch, list_entry(cl->children.next,
                                                  struct htb_class, sibling));
 
        /* note: this delete may happen twice (see htb_delete) */
-       list_del(&cl->hlist);
+       hlist_del_init(&cl->hlist);
        list_del(&cl->sibling);
 
        if (cl->prio_activity)
                htb_deactivate(q, cl);
 
        if (cl->cmode != HTB_CAN_SEND)
-               rb_erase(&cl->pq_node, q->wait_pq + cl->level);
+               htb_safe_rb_erase(&cl->pq_node, q->wait_pq + cl->level);
 
        kfree(cl);
 }
@@ -1286,15 +1237,12 @@ static void htb_destroy(struct Qdisc *sch)
 {
        struct htb_sched *q = qdisc_priv(sch);
 
-       del_timer_sync(&q->timer);
-#ifdef HTB_RATECM
-       del_timer_sync(&q->rttim);
-#endif
+       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 
+          and surprisingly it worked in 2.4. But it must precede it
           because filter need its target class alive to be able to call
           unbind_filter on it (without Oops). */
-       htb_destroy_filters(&q->filter_list);
+       tcf_destroy_chain(q->filter_list);
 
        while (!list_empty(&q->root))
                htb_destroy_class(sch, list_entry(q->root.next,
@@ -1307,6 +1255,9 @@ static int htb_delete(struct Qdisc *sch, unsigned long arg)
 {
        struct htb_sched *q = qdisc_priv(sch);
        struct htb_class *cl = (struct htb_class *)arg;
+       unsigned int qlen;
+       struct Qdisc *new_q = NULL;
+       int last_child = 0;
 
        // TODO: why don't allow to delete subtree ? references ? does
        // tc subsys quarantee us that in htb_destroy it holds no class
@@ -1314,13 +1265,29 @@ static int htb_delete(struct Qdisc *sch, unsigned long arg)
        if (!list_empty(&cl->children) || cl->filter_cnt)
                return -EBUSY;
 
+       if (!cl->level && htb_parent_last_child(cl)) {
+               new_q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops,
+                                               cl->parent->classid);
+               last_child = 1;
+       }
+
        sch_tree_lock(sch);
 
+       if (!cl->level) {
+               qlen = cl->un.leaf.q->q.qlen;
+               qdisc_reset(cl->un.leaf.q);
+               qdisc_tree_decrease_qlen(cl->un.leaf.q, qlen);
+       }
+
        /* delete from hash and active; remainder in destroy_class */
-       list_del_init(&cl->hlist);
+       hlist_del_init(&cl->hlist);
+
        if (cl->prio_activity)
                htb_deactivate(q, cl);
 
+       if (last_child)
+               htb_parent_to_leaf(cl, new_q);
+
        if (--cl->refcnt == 0)
                htb_destroy_class(sch, cl);
 
@@ -1337,34 +1304,57 @@ static void htb_put(struct Qdisc *sch, unsigned long arg)
 }
 
 static int htb_change_class(struct Qdisc *sch, u32 classid,
-                           u32 parentid, struct rtattr **tca,
+                           u32 parentid, struct nlattr **tca,
                            unsigned long *arg)
 {
        int err = -EINVAL;
        struct htb_sched *q = qdisc_priv(sch);
        struct htb_class *cl = (struct htb_class *)*arg, *parent;
-       struct rtattr *opt = tca[TCA_OPTIONS - 1];
+       struct nlattr *opt = tca[TCA_OPTIONS];
        struct qdisc_rate_table *rtab = NULL, *ctab = NULL;
-       struct rtattr *tb[TCA_HTB_RTAB];
+       struct nlattr *tb[TCA_HTB_RTAB + 1];
        struct tc_htb_opt *hopt;
 
        /* extract all subattrs from opt attr */
-       if (!opt || rtattr_parse_nested(tb, TCA_HTB_RTAB, opt) ||
-           tb[TCA_HTB_PARMS - 1] == NULL ||
-           RTA_PAYLOAD(tb[TCA_HTB_PARMS - 1]) < sizeof(*hopt))
+       if (!opt)
+               goto failure;
+
+       err = nla_parse_nested(tb, TCA_HTB_RTAB, opt, NULL);
+       if (err < 0)
+               goto failure;
+
+       err = -EINVAL;
+       if (tb[TCA_HTB_PARMS] == NULL ||
+           nla_len(tb[TCA_HTB_PARMS]) < sizeof(*hopt))
                goto failure;
 
        parent = parentid == TC_H_ROOT ? NULL : htb_find(parentid, sch);
 
-       hopt = RTA_DATA(tb[TCA_HTB_PARMS - 1]);
+       hopt = nla_data(tb[TCA_HTB_PARMS]);
 
-       rtab = qdisc_get_rtab(&hopt->rate, tb[TCA_HTB_RTAB - 1]);
-       ctab = qdisc_get_rtab(&hopt->ceil, tb[TCA_HTB_CTAB - 1]);
+       rtab = qdisc_get_rtab(&hopt->rate, tb[TCA_HTB_RTAB]);
+       ctab = qdisc_get_rtab(&hopt->ceil, tb[TCA_HTB_CTAB]);
        if (!rtab || !ctab)
                goto failure;
 
        if (!cl) {              /* new class */
                struct Qdisc *new_q;
+               int prio;
+               struct {
+                       struct nlattr           nla;
+                       struct gnet_estimator   opt;
+               } est = {
+                       .nla = {
+                               .nla_len        = nla_attr_size(sizeof(est.opt)),
+                               .nla_type       = TCA_RATE,
+                       },
+                       .opt = {
+                               /* 4s interval, 16s averaging constant */
+                               .interval       = 2,
+                               .ewma_log       = 2,
+                       },
+               };
+
                /* check for valid classid */
                if (!classid || TC_H_MAJ(classid ^ sch->handle)
                    || htb_find(classid, sch))
@@ -1379,27 +1369,37 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
                if ((cl = kzalloc(sizeof(*cl), GFP_KERNEL)) == NULL)
                        goto failure;
 
+               gen_new_estimator(&cl->bstats, &cl->rate_est,
+                                 &sch->dev->queue_lock,
+                                 tca[TCA_RATE] ? : &est.nla);
                cl->refcnt = 1;
                INIT_LIST_HEAD(&cl->sibling);
-               INIT_LIST_HEAD(&cl->hlist);
+               INIT_HLIST_NODE(&cl->hlist);
                INIT_LIST_HEAD(&cl->children);
                INIT_LIST_HEAD(&cl->un.leaf.drop_list);
+               RB_CLEAR_NODE(&cl->pq_node);
+
+               for (prio = 0; prio < TC_HTB_NUMPRIO; prio++)
+                       RB_CLEAR_NODE(&cl->node[prio]);
 
                /* create leaf qdisc early because it uses kmalloc(GFP_KERNEL)
                   so that can't be used inside of sch_tree_lock
                   -- thanks to Karlis Peisenieks */
-               new_q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops);
+               new_q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, classid);
                sch_tree_lock(sch);
                if (parent && !parent->level) {
+                       unsigned int qlen = parent->un.leaf.q->q.qlen;
+
                        /* turn parent into inner node */
-                       sch->q.qlen -= parent->un.leaf.q->q.qlen;
+                       qdisc_reset(parent->un.leaf.q);
+                       qdisc_tree_decrease_qlen(parent->un.leaf.q, qlen);
                        qdisc_destroy(parent->un.leaf.q);
                        if (parent->prio_activity)
                                htb_deactivate(q, parent);
 
                        /* remove from evt list because of level change */
                        if (parent->cmode != HTB_CAN_SEND) {
-                               rb_erase(&parent->pq_node, q->wait_pq);
+                               htb_safe_rb_erase(&parent->pq_node, q->wait_pq);
                                parent->cmode = HTB_CAN_SEND;
                        }
                        parent->level = (parent->parent ? parent->parent->level
@@ -1415,16 +1415,21 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
                /* set class to be in HTB_CAN_SEND state */
                cl->tokens = hopt->buffer;
                cl->ctokens = hopt->cbuffer;
-               cl->mbuffer = PSCHED_JIFFIE2US(HZ * 60);        /* 1min */
-               PSCHED_GET_TIME(cl->t_c);
+               cl->mbuffer = 60 * PSCHED_TICKS_PER_SEC;        /* 1min */
+               cl->t_c = psched_get_time();
                cl->cmode = HTB_CAN_SEND;
 
                /* attach to the hash list and parent's family */
-               list_add_tail(&cl->hlist, q->hash + htb_hash(classid));
+               hlist_add_head(&cl->hlist, q->hash + htb_hash(classid));
                list_add_tail(&cl->sibling,
                              parent ? &parent->children : &q->root);
-       } else
+       } else {
+               if (tca[TCA_RATE])
+                       gen_replace_estimator(&cl->bstats, &cl->rate_est,
+                                             &sch->dev->queue_lock,
+                                             tca[TCA_RATE]);
                sch_tree_lock(sch);
+       }
 
        /* it used to be a nasty bug here, we have to check that node
           is really leaf before changing cl->un.leaf ! */
@@ -1446,6 +1451,10 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
                        cl->un.leaf.quantum = hopt->quantum;
                if ((cl->un.leaf.prio = hopt->prio) >= TC_HTB_NUMPRIO)
                        cl->un.leaf.prio = TC_HTB_NUMPRIO - 1;
+
+               /* backup for htb_parent_to_leaf */
+               cl->quantum = cl->un.leaf.quantum;
+               cl->prio = cl->un.leaf.prio;
        }
 
        cl->buffer = hopt->buffer;
@@ -1520,10 +1529,10 @@ static void htb_walk(struct Qdisc *sch, struct qdisc_walker *arg)
                return;
 
        for (i = 0; i < HTB_HSIZE; i++) {
-               struct list_head *p;
-               list_for_each(p, q->hash + i) {
-                       struct htb_class *cl =
-                           list_entry(p, struct htb_class, hlist);
+               struct hlist_node *p;
+               struct htb_class *cl;
+
+               hlist_for_each_entry(cl, p, q->hash + i, hlist) {
                        if (arg->count < arg->skip) {
                                arg->count++;
                                continue;
@@ -1537,9 +1546,10 @@ static void htb_walk(struct Qdisc *sch, struct qdisc_walker *arg)
        }
 }
 
-static struct Qdisc_class_ops htb_class_ops = {
+static const struct Qdisc_class_ops htb_class_ops = {
        .graft          =       htb_graft,
        .leaf           =       htb_leaf,
+       .qlen_notify    =       htb_qlen_notify,
        .get            =       htb_get,
        .put            =       htb_put,
        .change         =       htb_change_class,
@@ -1552,7 +1562,7 @@ static struct Qdisc_class_ops htb_class_ops = {
        .dump_stats     =       htb_dump_class_stats,
 };
 
-static struct Qdisc_ops htb_qdisc_ops = {
+static struct Qdisc_ops htb_qdisc_ops __read_mostly = {
        .next           =       NULL,
        .cl_ops         =       &htb_class_ops,
        .id             =       "htb",