fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / net / sched / sch_hfsc.c
index 84ef3ab..6eefa69 100644 (file)
@@ -50,7 +50,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/errno.h>
@@ -62,6 +61,7 @@
 #include <linux/slab.h>
 #include <linux/timer.h>
 #include <linux/list.h>
+#include <linux/rbtree.h>
 #include <linux/init.h>
 #include <linux/netdevice.h>
 #include <linux/rtnetlink.h>
@@ -121,7 +121,9 @@ struct hfsc_class
        u32             classid;        /* class id */
        unsigned int    refcnt;         /* usage count */
 
-       struct tc_stats stats;          /* generic statistics */
+       struct gnet_stats_basic bstats;
+       struct gnet_stats_queue qstats;
+       struct gnet_stats_rate_est rate_est;
        spinlock_t      *stats_lock;
        unsigned int    level;          /* class level in hierarchy */
        struct tcf_proto *filter_list;  /* filter list */
@@ -133,9 +135,11 @@ struct hfsc_class
        struct list_head children;      /* child classes */
        struct Qdisc    *qdisc;         /* leaf qdisc */
 
-       struct list_head actlist;       /* active children list */
-       struct list_head alist;         /* active children list member */
-       struct list_head ellist;        /* eligible list member */
+       struct rb_node el_node;         /* qdisc's eligible tree member */
+       struct rb_root vt_tree;         /* active children sorted by cl_vt */
+       struct rb_node vt_node;         /* parent's vt_tree member */
+       struct rb_root cf_tree;         /* active children sorted by cl_f */
+       struct rb_node cf_node;         /* parent's cf_heap member */
        struct list_head hlist;         /* hash list member */
        struct list_head dlist;         /* drop list member */
 
@@ -161,6 +165,9 @@ struct hfsc_class
                                           adjustment */
        u64     cl_vtoff;               /* inter-period cumulative vt offset */
        u64     cl_cvtmax;              /* max child's vt in the last period */
+       u64     cl_cvtoff;              /* cumulative cvtmax of all periods */
+       u64     cl_pcvtoff;             /* parent's cvtoff at initalization
+                                          time */
 
        struct internal_sc cl_rsc;      /* internal real-time service curve */
        struct internal_sc cl_fsc;      /* internal fair service curve */
@@ -183,7 +190,7 @@ struct hfsc_sched
        u16     defcls;                         /* default class id */
        struct hfsc_class root;                 /* root class */
        struct list_head clhash[HFSC_HSIZE];    /* class hash */
-       struct list_head eligible;              /* eligible list */
+       struct rb_root eligible;                /* eligible tree */
        struct list_head droplist;              /* active leaf class list (for
                                                   dropping) */
        struct sk_buff_head requeue;            /* requeued packet */
@@ -200,7 +207,7 @@ struct hfsc_sched
 do {                                                                   \
        struct timeval tv;                                              \
        do_gettimeofday(&tv);                                           \
-       (stamp) = 1000000ULL * tv.tv_sec + tv.tv_usec;                  \
+       (stamp) = 1ULL * USEC_PER_SEC * tv.tv_sec + tv.tv_usec;         \
 } while (0)
 #endif
 
@@ -219,82 +226,51 @@ do {                                                                      \
 
 
 /*
- * eligible list holds backlogged classes being sorted by their eligible times.
- * there is one eligible list per hfsc instance.
+ * eligible tree holds backlogged classes being sorted by their eligible times.
+ * there is one eligible tree per hfsc instance.
  */
 
 static void
-ellist_insert(struct hfsc_class *cl)
+eltree_insert(struct hfsc_class *cl)
 {
-       struct list_head *head = &cl->sched->eligible;
-       struct hfsc_class *p;
+       struct rb_node **p = &cl->sched->eligible.rb_node;
+       struct rb_node *parent = NULL;
+       struct hfsc_class *cl1;
 
-       /* check the last entry first */
-       if (list_empty(head) ||
-           ((p = list_entry(head->prev, struct hfsc_class, ellist)) &&
-            p->cl_e <= cl->cl_e)) {
-               list_add_tail(&cl->ellist, head);
-               return;
-       }
-
-       list_for_each_entry(p, head, ellist) {
-               if (cl->cl_e < p->cl_e) {
-                       /* insert cl before p */
-                       list_add_tail(&cl->ellist, &p->ellist);
-                       return;
-               }
+       while (*p != NULL) {
+               parent = *p;
+               cl1 = rb_entry(parent, struct hfsc_class, el_node);
+               if (cl->cl_e >= cl1->cl_e)
+                       p = &parent->rb_right;
+               else
+                       p = &parent->rb_left;
        }
-       ASSERT(0); /* should not reach here */
+       rb_link_node(&cl->el_node, parent, p);
+       rb_insert_color(&cl->el_node, &cl->sched->eligible);
 }
 
 static inline void
-ellist_remove(struct hfsc_class *cl)
+eltree_remove(struct hfsc_class *cl)
 {
-       list_del(&cl->ellist);
+       rb_erase(&cl->el_node, &cl->sched->eligible);
 }
 
-static void
-ellist_update(struct hfsc_class *cl)
+static inline void
+eltree_update(struct hfsc_class *cl)
 {
-       struct list_head *head = &cl->sched->eligible;
-       struct hfsc_class *p, *last;
-
-       /*
-        * the eligible time of a class increases monotonically.
-        * if the next entry has a larger eligible time, nothing to do.
-        */
-       if (cl->ellist.next == head ||
-           ((p = list_entry(cl->ellist.next, struct hfsc_class, ellist)) &&
-            cl->cl_e <= p->cl_e))
-               return;
-
-       /* check the last entry */
-       last = list_entry(head->prev, struct hfsc_class, ellist);
-       if (last->cl_e <= cl->cl_e) {
-               list_move_tail(&cl->ellist, head);
-               return;
-       }
-
-       /*
-        * the new position must be between the next entry
-        * and the last entry
-        */
-       list_for_each_entry_continue(p, head, ellist) {
-               if (cl->cl_e < p->cl_e) {
-                       list_move_tail(&cl->ellist, &p->ellist);
-                       return;
-               }
-       }
-       ASSERT(0); /* should not reach here */
+       eltree_remove(cl);
+       eltree_insert(cl);
 }
 
 /* find the class with the minimum deadline among the eligible classes */
 static inline struct hfsc_class *
-ellist_get_mindl(struct list_head *head, u64 cur_time)
+eltree_get_mindl(struct hfsc_sched *q, u64 cur_time)
 {
        struct hfsc_class *p, *cl = NULL;
+       struct rb_node *n;
 
-       list_for_each_entry(p, head, ellist) {
+       for (n = rb_first(&q->eligible); n != NULL; n = rb_next(n)) {
+               p = rb_entry(n, struct hfsc_class, el_node);
                if (p->cl_e > cur_time)
                        break;
                if (cl == NULL || p->cl_d < cl->cl_d)
@@ -305,92 +281,62 @@ ellist_get_mindl(struct list_head *head, u64 cur_time)
 
 /* find the class with minimum eligible time among the eligible classes */
 static inline struct hfsc_class *
-ellist_get_minel(struct list_head *head)
+eltree_get_minel(struct hfsc_sched *q)
 {
-       if (list_empty(head))
+       struct rb_node *n;
+       
+       n = rb_first(&q->eligible);
+       if (n == NULL)
                return NULL;
-       return list_entry(head->next, struct hfsc_class, ellist);
+       return rb_entry(n, struct hfsc_class, el_node);
 }
 
 /*
- * active children list holds backlogged child classes being sorted
- * by their virtual time. each intermediate class has one active
- * children list.
+ * vttree holds holds backlogged child classes being sorted by their virtual
+ * time. each intermediate class has one vttree.
  */
 static void
-actlist_insert(struct hfsc_class *cl)
+vttree_insert(struct hfsc_class *cl)
 {
-       struct list_head *head = &cl->cl_parent->actlist;
-       struct hfsc_class *p;
+       struct rb_node **p = &cl->cl_parent->vt_tree.rb_node;
+       struct rb_node *parent = NULL;
+       struct hfsc_class *cl1;
 
-       /* check the last entry first */
-       if (list_empty(head) ||
-           ((p = list_entry(head->prev, struct hfsc_class, alist)) &&
-            p->cl_vt <= cl->cl_vt)) {
-               list_add_tail(&cl->alist, head);
-               return;
-       }
-
-       list_for_each_entry(p, head, alist) {
-               if (cl->cl_vt < p->cl_vt) {
-                       /* insert cl before p */
-                       list_add_tail(&cl->alist, &p->alist);
-                       return;
-               }
+       while (*p != NULL) {
+               parent = *p;
+               cl1 = rb_entry(parent, struct hfsc_class, vt_node);
+               if (cl->cl_vt >= cl1->cl_vt)
+                       p = &parent->rb_right;
+               else
+                       p = &parent->rb_left;
        }
-       ASSERT(0); /* should not reach here */
+       rb_link_node(&cl->vt_node, parent, p);
+       rb_insert_color(&cl->vt_node, &cl->cl_parent->vt_tree);
 }
 
 static inline void
-actlist_remove(struct hfsc_class *cl)
+vttree_remove(struct hfsc_class *cl)
 {
-       list_del(&cl->alist);
+       rb_erase(&cl->vt_node, &cl->cl_parent->vt_tree);
 }
 
-static void
-actlist_update(struct hfsc_class *cl)
+static inline void
+vttree_update(struct hfsc_class *cl)
 {
-       struct list_head *head = &cl->cl_parent->actlist;
-       struct hfsc_class *p, *last;
-
-       /*
-        * the virtual time of a class increases monotonically.
-        * if the next entry has a larger virtual time, nothing to do.
-        */
-       if (cl->alist.next == head ||
-           ((p = list_entry(cl->alist.next, struct hfsc_class, alist)) &&
-            cl->cl_vt <= p->cl_vt))
-               return;
-
-       /* check the last entry */
-       last = list_entry(head->prev, struct hfsc_class, alist);
-       if (last->cl_vt <= cl->cl_vt) {
-               list_move_tail(&cl->alist, head);
-               return;
-       }
-
-       /*
-        * the new position must be between the next entry
-        * and the last entry
-        */
-       list_for_each_entry_continue(p, head, alist) {
-               if (cl->cl_vt < p->cl_vt) {
-                       list_move_tail(&cl->alist, &p->alist);
-                       return;
-               }
-       }
-       ASSERT(0); /* should not reach here */
+       vttree_remove(cl);
+       vttree_insert(cl);
 }
 
 static inline struct hfsc_class *
-actlist_firstfit(struct hfsc_class *cl, u64 cur_time)
+vttree_firstfit(struct hfsc_class *cl, u64 cur_time)
 {
        struct hfsc_class *p;
+       struct rb_node *n;
 
-       list_for_each_entry(p, &cl->actlist, alist) {
-               if (p->cl_f <= cur_time) {
+       for (n = rb_first(&cl->vt_tree); n != NULL; n = rb_next(n)) {
+               p = rb_entry(n, struct hfsc_class, vt_node);
+               if (p->cl_f <= cur_time)
                        return p;
-               }
        }
        return NULL;
 }
@@ -399,14 +345,14 @@ actlist_firstfit(struct hfsc_class *cl, u64 cur_time)
  * get the leaf class with the minimum vt in the hierarchy
  */
 static struct hfsc_class *
-actlist_get_minvt(struct hfsc_class *cl, u64 cur_time)
+vttree_get_minvt(struct hfsc_class *cl, u64 cur_time)
 {
        /* if root-class's cfmin is bigger than cur_time nothing to do */
        if (cl->cl_cfmin > cur_time)
                return NULL;
 
        while (cl->level > 0) {
-               cl = actlist_firstfit(cl, cur_time);
+               cl = vttree_firstfit(cl, cur_time);
                if (cl == NULL)
                        return NULL;
                /*
@@ -418,6 +364,38 @@ actlist_get_minvt(struct hfsc_class *cl, u64 cur_time)
        return cl;
 }
 
+static void
+cftree_insert(struct hfsc_class *cl)
+{
+       struct rb_node **p = &cl->cl_parent->cf_tree.rb_node;
+       struct rb_node *parent = NULL;
+       struct hfsc_class *cl1;
+
+       while (*p != NULL) {
+               parent = *p;
+               cl1 = rb_entry(parent, struct hfsc_class, cf_node);
+               if (cl->cl_f >= cl1->cl_f)
+                       p = &parent->rb_right;
+               else
+                       p = &parent->rb_left;
+       }
+       rb_link_node(&cl->cf_node, parent, p);
+       rb_insert_color(&cl->cf_node, &cl->cl_parent->cf_tree);
+}
+
+static inline void
+cftree_remove(struct hfsc_class *cl)
+{
+       rb_erase(&cl->cf_node, &cl->cl_parent->cf_tree);
+}
+
+static inline void
+cftree_update(struct hfsc_class *cl)
+{
+       cftree_remove(cl);
+       cftree_insert(cl);
+}
+
 /*
  * service curve support functions
  *
@@ -523,8 +501,8 @@ d2dx(u32 d)
        u64 dx;
 
        dx = ((u64)d * PSCHED_JIFFIE2US(HZ));
-       dx += 1000000 - 1;
-       do_div(dx, 1000000);
+       dx += USEC_PER_SEC - 1;
+       do_div(dx, USEC_PER_SEC);
        return dx;
 }
 
@@ -544,7 +522,7 @@ dx2d(u64 dx)
 {
        u64 d;
 
-       d = dx * 1000000;
+       d = dx * USEC_PER_SEC;
        do_div(d, PSCHED_JIFFIE2US(HZ));
        return (u32)d;
 }
@@ -711,7 +689,7 @@ init_ed(struct hfsc_class *cl, unsigned int next_len)
        cl->cl_e = rtsc_y2x(&cl->cl_eligible, cl->cl_cumul);
        cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
 
-       ellist_insert(cl);
+       eltree_insert(cl);
 }
 
 static void
@@ -720,7 +698,7 @@ update_ed(struct hfsc_class *cl, unsigned int next_len)
        cl->cl_e = rtsc_y2x(&cl->cl_eligible, cl->cl_cumul);
        cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
 
-       ellist_update(cl);
+       eltree_update(cl);
 }
 
 static inline void
@@ -729,32 +707,25 @@ update_d(struct hfsc_class *cl, unsigned int next_len)
        cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
 }
 
-static void
+static inline void
 update_cfmin(struct hfsc_class *cl)
 {
+       struct rb_node *n = rb_first(&cl->cf_tree);
        struct hfsc_class *p;
-       u64 cfmin;
 
-       if (list_empty(&cl->actlist)) {
+       if (n == NULL) {
                cl->cl_cfmin = 0;
                return;
        }
-       cfmin = HT_INFINITY;
-       list_for_each_entry(p, &cl->actlist, alist) {
-               if (p->cl_f == 0) {
-                       cl->cl_cfmin = 0;
-                       return;
-               }
-               if (p->cl_f < cfmin)
-                       cfmin = p->cl_f;
-       }
-       cl->cl_cfmin = cfmin;
+       p = rb_entry(n, struct hfsc_class, cf_node);
+       cl->cl_cfmin = p->cl_f;
 }
 
 static void
 init_vf(struct hfsc_class *cl, unsigned int len)
 {
-       struct hfsc_class *max_cl, *p;
+       struct hfsc_class *max_cl;
+       struct rb_node *n;
        u64 vt, f, cur_time;
        int go_active;
 
@@ -767,9 +738,9 @@ init_vf(struct hfsc_class *cl, unsigned int len)
                        go_active = 0;
 
                if (go_active) {
-                       if (!list_empty(&cl->cl_parent->actlist)) {
-                               max_cl = list_entry(cl->cl_parent->actlist.prev,
-                                                   struct hfsc_class, alist);
+                       n = rb_last(&cl->cl_parent->vt_tree);
+                       if (n != NULL) {
+                               max_cl = rb_entry(n, struct hfsc_class,vt_node);
                                /*
                                 * set vt to the average of the min and max
                                 * classes.  if the parent's period didn't
@@ -785,19 +756,20 @@ init_vf(struct hfsc_class *cl, unsigned int len)
                        } else {
                                /*
                                 * first child for a new parent backlog period.
-                                * add parent's cvtmax to vtoff of children
-                                * to make a new vt (vtoff + vt) larger than
-                                * the vt in the last period for all children.
+                                * add parent's cvtmax to cvtoff to make a new
+                                * vt (vtoff + vt) larger than the vt in the
+                                * last period for all children.
                                 */
                                vt = cl->cl_parent->cl_cvtmax;
-                               list_for_each_entry(p, &cl->cl_parent->children,
-                                                                      siblings)
-                                       p->cl_vtoff += vt;
-                               cl->cl_vt = 0;
+                               cl->cl_parent->cl_cvtoff += vt;
                                cl->cl_parent->cl_cvtmax = 0;
                                cl->cl_parent->cl_cvtmin = 0;
+                               cl->cl_vt = 0;
                        }
 
+                       cl->cl_vtoff = cl->cl_parent->cl_cvtoff -
+                                                       cl->cl_pcvtoff;
+
                        /* update the virtual curve */
                        vt = cl->cl_vt + cl->cl_vtoff;
                        rtsc_min(&cl->cl_virtual, &cl->cl_fsc, vt,
@@ -814,7 +786,8 @@ init_vf(struct hfsc_class *cl, unsigned int len)
                                cl->cl_parentperiod++;
                        cl->cl_f = 0;
 
-                       actlist_insert(cl);
+                       vttree_insert(cl);
+                       cftree_insert(cl);
 
                        if (cl->cl_flags & HFSC_USC) {
                                /* class has upper limit curve */
@@ -834,6 +807,7 @@ init_vf(struct hfsc_class *cl, unsigned int len)
                f = max(cl->cl_myf, cl->cl_cfmin);
                if (f != cl->cl_f) {
                        cl->cl_f = f;
+                       cftree_update(cl);
                        update_cfmin(cl->cl_parent);
                }
        }
@@ -866,9 +840,10 @@ update_vf(struct hfsc_class *cl, unsigned int len, u64 cur_time)
                        if (cl->cl_vt > cl->cl_parent->cl_cvtmax)
                                cl->cl_parent->cl_cvtmax = cl->cl_vt;
 
-                       /* remove this class from the vt list */
-                       actlist_remove(cl);
+                       /* remove this class from the vt tree */
+                       vttree_remove(cl);
 
+                       cftree_remove(cl);
                        update_cfmin(cl->cl_parent);
 
                        continue;
@@ -890,8 +865,8 @@ update_vf(struct hfsc_class *cl, unsigned int len, u64 cur_time)
                        cl->cl_vt = cl->cl_parent->cl_cvtmin;
                }
 
-               /* update the vt list */
-               actlist_update(cl);
+               /* update the vt tree */
+               vttree_update(cl);
 
                if (cl->cl_flags & HFSC_USC) {
                        cl->cl_myf = cl->cl_myfadj + rtsc_y2x(&cl->cl_ulimit,
@@ -921,6 +896,7 @@ update_vf(struct hfsc_class *cl, unsigned int len, u64 cur_time)
                f = max(cl->cl_myf, cl->cl_cfmin);
                if (f != cl->cl_f) {
                        cl->cl_f = f;
+                       cftree_update(cl);
                        update_cfmin(cl->cl_parent);
                }
        }
@@ -941,13 +917,13 @@ static void
 set_passive(struct hfsc_class *cl)
 {
        if (cl->cl_flags & HFSC_RSC)
-               ellist_remove(cl);
+               eltree_remove(cl);
 
        list_del(&cl->dlist);
 
        /*
-        * actlist is now handled in update_vf() so that update_vf(cl, 0, 0)
-        * needs to be called explicitly to remove a class from actlist
+        * vttree is now handled in update_vf() so that update_vf(cl, 0, 0)
+        * needs to be called explicitly to remove a class from vttree.
         */
 }
 
@@ -970,6 +946,7 @@ qdisc_peek_len(struct Qdisc *sch)
        if (unlikely(sch->ops->requeue(skb, sch) != NET_XMIT_SUCCESS)) {
                if (net_ratelimit())
                        printk("qdisc_peek_len: failed to requeue\n");
+               qdisc_tree_decrease_qlen(sch, 1);
                return 0;
        }
        return len;
@@ -981,11 +958,7 @@ hfsc_purge_queue(struct Qdisc *sch, struct hfsc_class *cl)
        unsigned int len = cl->qdisc->q.qlen;
 
        qdisc_reset(cl->qdisc);
-       if (len > 0) {
-               update_vf(cl, 0, 0);
-               set_passive(cl);
-               sch->q.qlen -= len;
-       }
+       qdisc_tree_decrease_qlen(cl->qdisc, len);
 }
 
 static void
@@ -997,10 +970,10 @@ hfsc_adjust_levels(struct hfsc_class *cl)
        do {
                level = 0;
                list_for_each_entry(p, &cl->children, siblings) {
-                       if (p->level > level)
-                               level = p->level;
+                       if (p->level >= level)
+                               level = p->level + 1;
                }
-               cl->level = level + 1;
+               cl->level = level;
        } while ((cl = cl->cl_parent) != NULL);
 }
 
@@ -1069,8 +1042,7 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
        struct tc_service_curve *rsc = NULL, *fsc = NULL, *usc = NULL;
        u64 cur_time;
 
-       if (opt == NULL ||
-           rtattr_parse(tb, TCA_HFSC_MAX, RTA_DATA(opt), RTA_PAYLOAD(opt)))
+       if (opt == NULL || rtattr_parse_nested(tb, TCA_HFSC_MAX, opt))
                return -EINVAL;
 
        if (tb[TCA_HFSC_RSC-1]) {
@@ -1123,11 +1095,9 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
                sch_tree_unlock(sch);
 
 #ifdef CONFIG_NET_ESTIMATOR
-               if (tca[TCA_RATE-1]) {
-                       qdisc_kill_estimator(&cl->stats);
-                       qdisc_new_estimator(&cl->stats, cl->stats_lock,
-                                           tca[TCA_RATE-1]);
-               }
+               if (tca[TCA_RATE-1])
+                       gen_replace_estimator(&cl->bstats, &cl->rate_est,
+                               cl->stats_lock, tca[TCA_RATE-1]);
 #endif
                return 0;
        }
@@ -1150,10 +1120,9 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
        if (rsc == NULL && fsc == NULL)
                return -EINVAL;
 
-       cl = kmalloc(sizeof(struct hfsc_class), GFP_KERNEL);
+       cl = kzalloc(sizeof(struct hfsc_class), GFP_KERNEL);
        if (cl == NULL)
                return -ENOBUFS;
-       memset(cl, 0, sizeof(struct hfsc_class));
 
        if (rsc != NULL)
                hfsc_change_rsc(cl, rsc, 0);
@@ -1166,12 +1135,13 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
        cl->classid   = classid;
        cl->sched     = q;
        cl->cl_parent = parent;
-       cl->qdisc = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops);
+       cl->qdisc = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, classid);
        if (cl->qdisc == NULL)
                cl->qdisc = &noop_qdisc;
        cl->stats_lock = &sch->dev->queue_lock;
        INIT_LIST_HEAD(&cl->children);
-       INIT_LIST_HEAD(&cl->actlist);
+       cl->vt_tree = RB_ROOT;
+       cl->cf_tree = RB_ROOT;
 
        sch_tree_lock(sch);
        list_add_tail(&cl->hlist, &q->clhash[hfsc_hash(classid)]);
@@ -1179,12 +1149,13 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
        if (parent->level == 0)
                hfsc_purge_queue(sch, parent);
        hfsc_adjust_levels(parent);
+       cl->cl_pcvtoff = parent->cl_cvtoff;
        sch_tree_unlock(sch);
 
 #ifdef CONFIG_NET_ESTIMATOR
        if (tca[TCA_RATE-1])
-               qdisc_new_estimator(&cl->stats, cl->stats_lock,
-                                   tca[TCA_RATE-1]);
+               gen_new_estimator(&cl->bstats, &cl->rate_est,
+                       cl->stats_lock, tca[TCA_RATE-1]);
 #endif
        *arg = (unsigned long)cl;
        return 0;
@@ -1209,7 +1180,7 @@ hfsc_destroy_class(struct Qdisc *sch, struct hfsc_class *cl)
        hfsc_destroy_filters(&cl->filter_list);
        qdisc_destroy(cl->qdisc);
 #ifdef CONFIG_NET_ESTIMATOR
-       qdisc_kill_estimator(&cl->stats);
+       gen_kill_estimator(&cl->bstats, &cl->rate_est);
 #endif
        if (cl != &q->root)
                kfree(cl);
@@ -1238,7 +1209,7 @@ hfsc_delete_class(struct Qdisc *sch, unsigned long arg)
 }
 
 static struct hfsc_class *
-hfsc_classify(struct sk_buff *skb, struct Qdisc *sch, int *qres)
+hfsc_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
 {
        struct hfsc_sched *q = qdisc_priv(sch);
        struct hfsc_class *cl;
@@ -1251,35 +1222,20 @@ hfsc_classify(struct sk_buff *skb, struct Qdisc *sch, int *qres)
                if (cl->level == 0)
                        return cl;
 
+       *qerr = NET_XMIT_BYPASS;
        tcf = q->root.filter_list;
        while (tcf && (result = tc_classify(skb, tcf, &res)) >= 0) {
 #ifdef CONFIG_NET_CLS_ACT
-               int terminal = 0;
                switch (result) {
-               case TC_ACT_SHOT: 
-                       *qres = NET_XMIT_DROP;
-                       terminal = 1;
-                       break;
                case TC_ACT_QUEUED:
                case TC_ACT_STOLEN: 
-                       terminal = 1;
-                       break;
-               case TC_ACT_RECLASSIFY: 
-               case TC_ACT_OK:
-               case TC_ACT_UNSPEC:
-               default:
-               break;
-               }
-
-               if (terminal) {
-                       kfree_skb(skb);
+                       *qerr = NET_XMIT_SUCCESS;
+               case TC_ACT_SHOT: 
                        return NULL;
                }
-#else
-#ifdef CONFIG_NET_CLS_POLICE
+#elif defined(CONFIG_NET_CLS_POLICE)
                if (result == TC_POLICE_SHOT)
                        return NULL;
-#endif
 #endif
                if ((cl = (struct hfsc_class *)res.class) == NULL) {
                        if ((cl = hfsc_find_class(res.classid, sch)) == NULL)
@@ -1312,7 +1268,8 @@ hfsc_graft_class(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
        if (cl->level > 0)
                return -EINVAL;
        if (new == NULL) {
-               new = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops);
+               new = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops,
+                                       cl->classid);
                if (new == NULL)
                        new = &noop_qdisc;
        }
@@ -1335,6 +1292,17 @@ hfsc_class_leaf(struct Qdisc *sch, unsigned long arg)
        return NULL;
 }
 
+static void
+hfsc_qlen_notify(struct Qdisc *sch, unsigned long arg)
+{
+       struct hfsc_class *cl = (struct hfsc_class *)arg;
+
+       if (cl->qdisc->q.qlen == 0) {
+               update_vf(cl, 0, 0);
+               set_passive(cl);
+       }
+}
+
 static unsigned long
 hfsc_get_class(struct Qdisc *sch, u32 classid)
 {
@@ -1427,36 +1395,6 @@ hfsc_dump_curves(struct sk_buff *skb, struct hfsc_class *cl)
        return -1;
 }
 
-static inline int
-hfsc_dump_stats(struct sk_buff *skb, struct hfsc_class *cl)
-{
-       cl->stats.qlen = cl->qdisc->q.qlen;
-       if (qdisc_copy_stats(skb, &cl->stats, cl->stats_lock) < 0)
-               goto rtattr_failure;
-
-       return skb->len;
-
- rtattr_failure:
-       return -1;
-}
-
-static inline int
-hfsc_dump_xstats(struct sk_buff *skb, struct hfsc_class *cl)
-{
-       struct tc_hfsc_stats xstats;
-
-       xstats.level  = cl->level;
-       xstats.period = cl->cl_vtperiod;
-       xstats.work   = cl->cl_total;
-       xstats.rtwork = cl->cl_cumul;
-       RTA_PUT(skb, TCA_XSTATS, sizeof(xstats), &xstats);
-
-       return skb->len;
-
- rtattr_failure:
-       return -1;
-}
-
 static int
 hfsc_dump_class(struct Qdisc *sch, unsigned long arg, struct sk_buff *skb,
                 struct tcmsg *tcm)
@@ -1474,11 +1412,6 @@ hfsc_dump_class(struct Qdisc *sch, unsigned long arg, struct sk_buff *skb,
        if (hfsc_dump_curves(skb, cl) < 0)
                goto rtattr_failure;
        rta->rta_len = skb->tail - b;
-
-       if ((hfsc_dump_stats(skb, cl) < 0) ||
-           (hfsc_dump_xstats(skb, cl) < 0))
-               goto rtattr_failure;
-
        return skb->len;
 
  rtattr_failure:
@@ -1486,6 +1419,31 @@ hfsc_dump_class(struct Qdisc *sch, unsigned long arg, struct sk_buff *skb,
        return -1;
 }
 
+static int
+hfsc_dump_class_stats(struct Qdisc *sch, unsigned long arg,
+       struct gnet_dump *d)
+{
+       struct hfsc_class *cl = (struct hfsc_class *)arg;
+       struct tc_hfsc_stats xstats;
+
+       cl->qstats.qlen = cl->qdisc->q.qlen;
+       xstats.level   = cl->level;
+       xstats.period  = cl->cl_vtperiod;
+       xstats.work    = cl->cl_total;
+       xstats.rtwork  = cl->cl_cumul;
+
+       if (gnet_stats_copy_basic(d, &cl->bstats) < 0 ||
+#ifdef CONFIG_NET_ESTIMATOR
+           gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 ||
+#endif
+           gnet_stats_copy_queue(d, &cl->qstats) < 0)
+               return -1;
+
+       return gnet_stats_copy_app(d, &xstats, sizeof(xstats));
+}
+
+
+
 static void
 hfsc_walk(struct Qdisc *sch, struct qdisc_walker *arg)
 {
@@ -1528,7 +1486,7 @@ hfsc_schedule_watchdog(struct Qdisc *sch, u64 cur_time)
        u64 next_time = 0;
        long delay;
 
-       if ((cl = ellist_get_minel(&q->eligible)) != NULL)
+       if ((cl = eltree_get_minel(q)) != NULL)
                next_time = cl->cl_e;
        if (q->root.cl_cfmin != 0) {
                if (next_time == 0 || next_time > q->root.cl_cfmin)
@@ -1553,25 +1511,26 @@ hfsc_init_qdisc(struct Qdisc *sch, struct rtattr *opt)
                return -EINVAL;
        qopt = RTA_DATA(opt);
 
-       memset(q, 0, sizeof(struct hfsc_sched));
        sch->stats_lock = &sch->dev->queue_lock;
 
        q->defcls = qopt->defcls;
        for (i = 0; i < HFSC_HSIZE; i++)
                INIT_LIST_HEAD(&q->clhash[i]);
-       INIT_LIST_HEAD(&q->eligible);
+       q->eligible = RB_ROOT;
        INIT_LIST_HEAD(&q->droplist);
        skb_queue_head_init(&q->requeue);
 
        q->root.refcnt  = 1;
        q->root.classid = sch->handle;
        q->root.sched   = q;
-       q->root.qdisc = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops);
+       q->root.qdisc = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops,
+                                         sch->handle);
        if (q->root.qdisc == NULL)
                q->root.qdisc = &noop_qdisc;
        q->root.stats_lock = &sch->dev->queue_lock;
        INIT_LIST_HEAD(&q->root.children);
-       INIT_LIST_HEAD(&q->root.actlist);
+       q->root.vt_tree = RB_ROOT;
+       q->root.cf_tree = RB_ROOT;
 
        list_add(&q->root.hlist, &q->clhash[hfsc_hash(q->root.classid)]);
 
@@ -1611,6 +1570,8 @@ hfsc_reset_class(struct hfsc_class *cl)
        cl->cl_vtoff        = 0;
        cl->cl_cvtmin       = 0;
        cl->cl_cvtmax       = 0;
+       cl->cl_cvtoff       = 0;
+       cl->cl_pcvtoff      = 0;
        cl->cl_vtperiod     = 0;
        cl->cl_parentperiod = 0;
        cl->cl_f            = 0;
@@ -1618,7 +1579,9 @@ hfsc_reset_class(struct hfsc_class *cl)
        cl->cl_myfadj       = 0;
        cl->cl_cfmin        = 0;
        cl->cl_nactive      = 0;
-       INIT_LIST_HEAD(&cl->actlist);
+
+       cl->vt_tree = RB_ROOT;
+       cl->cf_tree = RB_ROOT;
        qdisc_reset(cl->qdisc);
 
        if (cl->cl_flags & HFSC_RSC)
@@ -1641,7 +1604,7 @@ hfsc_reset_qdisc(struct Qdisc *sch)
                        hfsc_reset_class(cl);
        }
        __skb_queue_purge(&q->requeue);
-       INIT_LIST_HEAD(&q->eligible);
+       q->eligible = RB_ROOT;
        INIT_LIST_HEAD(&q->droplist);
        del_timer(&q->wd_timer);
        sch->flags &= ~TCQ_F_THROTTLED;
@@ -1672,11 +1635,6 @@ hfsc_dump_qdisc(struct Qdisc *sch, struct sk_buff *skb)
 
        qopt.defcls = q->defcls;
        RTA_PUT(skb, TCA_OPTIONS, sizeof(qopt), &qopt);
-
-       sch->stats.qlen = sch->q.qlen;
-       if (qdisc_copy_stats(skb, &sch->stats, sch->stats_lock) < 0)
-               goto rtattr_failure;
-
        return skb->len;
 
  rtattr_failure:
@@ -1687,41 +1645,33 @@ hfsc_dump_qdisc(struct Qdisc *sch, struct sk_buff *skb)
 static int
 hfsc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 {
-       int ret = NET_XMIT_SUCCESS;
-       struct hfsc_class *cl = hfsc_classify(skb, sch, &ret);
-       unsigned int len = skb->len;
+       struct hfsc_class *cl;
+       unsigned int len;
        int err;
 
-
-#ifdef CONFIG_NET_CLS_ACT
-       if (cl == NULL) {
-               if (NET_XMIT_DROP == ret) {
-                       sch->stats.drops++;
-               }
-               return ret;
-       }
-#else
+       cl = hfsc_classify(skb, sch, &err);
        if (cl == NULL) {
+               if (err == NET_XMIT_BYPASS)
+                       sch->qstats.drops++;
                kfree_skb(skb);
-               sch->stats.drops++;
-               return NET_XMIT_DROP;
+               return err;
        }
-#endif
 
+       len = skb->len;
        err = cl->qdisc->enqueue(skb, cl->qdisc);
        if (unlikely(err != NET_XMIT_SUCCESS)) {
-               cl->stats.drops++;
-               sch->stats.drops++;
+               cl->qstats.drops++;
+               sch->qstats.drops++;
                return err;
        }
 
        if (cl->qdisc->q.qlen == 1)
                set_active(cl, len);
 
-       cl->stats.packets++;
-       cl->stats.bytes += len;
-       sch->stats.packets++;
-       sch->stats.bytes += len;
+       cl->bstats.packets++;
+       cl->bstats.bytes += len;
+       sch->bstats.packets++;
+       sch->bstats.bytes += len;
        sch->q.qlen++;
 
        return NET_XMIT_SUCCESS;
@@ -1749,16 +1699,16 @@ hfsc_dequeue(struct Qdisc *sch)
         * find the class with the minimum deadline among
         * the eligible classes.
         */
-       if ((cl = ellist_get_mindl(&q->eligible, cur_time)) != NULL) {
+       if ((cl = eltree_get_mindl(q, cur_time)) != NULL) {
                realtime = 1;
        } else {
                /*
                 * use link-sharing criteria
                 * get the class with the minimum vt in the hierarchy
                 */
-               cl = actlist_get_minvt(&q->root, cur_time);
+               cl = vttree_get_minvt(&q->root, cur_time);
                if (cl == NULL) {
-                       sch->stats.overlimits++;
+                       sch->qstats.overlimits++;
                        hfsc_schedule_watchdog(sch, cur_time);
                        return NULL;
                }
@@ -1803,6 +1753,7 @@ hfsc_requeue(struct sk_buff *skb, struct Qdisc *sch)
 
        __skb_queue_head(&q->requeue, skb);
        sch->q.qlen++;
+       sch->qstats.requeues++;
        return NET_XMIT_SUCCESS;
 }
 
@@ -1822,8 +1773,8 @@ hfsc_drop(struct Qdisc *sch)
                        } else {
                                list_move_tail(&cl->dlist, &q->droplist);
                        }
-                       cl->stats.drops++;
-                       sch->stats.drops++;
+                       cl->qstats.drops++;
+                       sch->qstats.drops++;
                        sch->q.qlen--;
                        return len;
                }
@@ -1836,12 +1787,14 @@ static struct Qdisc_class_ops hfsc_class_ops = {
        .delete         = hfsc_delete_class,
        .graft          = hfsc_graft_class,
        .leaf           = hfsc_class_leaf,
+       .qlen_notify    = hfsc_qlen_notify,
        .get            = hfsc_get_class,
        .put            = hfsc_put_class,
        .bind_tcf       = hfsc_bind_tcf,
        .unbind_tcf     = hfsc_unbind_tcf,
        .tcf_chain      = hfsc_tcf_chain,
        .dump           = hfsc_dump_class,
+       .dump_stats     = hfsc_dump_class_stats,
        .walk           = hfsc_walk
 };