vserver 1.9.5.x5
[linux-2.6.git] / net / sched / sch_netem.c
index d29df1c..c8259cb 100644 (file)
@@ -15,7 +15,7 @@
 
 #include <linux/config.h>
 #include <linux/module.h>
-#include <asm/bitops.h>
+#include <linux/bitops.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
@@ -142,23 +142,27 @@ static int delay_skb(struct Qdisc *sch, struct sk_buff *skb)
 {
        struct netem_sched_data *q = qdisc_priv(sch);
        struct netem_skb_cb *cb = (struct netem_skb_cb *)skb->cb;
+       psched_tdiff_t td;
        psched_time_t now;
        
        PSCHED_GET_TIME(now);
-       PSCHED_TADD2(now, tabledist(q->latency, q->jitter, 
-                                   &q->delay_cor, q->delay_dist),
-                    cb->time_to_send);
+       td = tabledist(q->latency, q->jitter, &q->delay_cor, q->delay_dist);
+       PSCHED_TADD2(now, td, cb->time_to_send);
        
        /* Always queue at tail to keep packets in order */
        if (likely(q->delayed.qlen < q->limit)) {
                __skb_queue_tail(&q->delayed, skb);
-               sch->q.qlen++;
-               sch->stats.bytes += skb->len;
-               sch->stats.packets++;
+               sch->bstats.bytes += skb->len;
+               sch->bstats.packets++;
+
+               if (!timer_pending(&q->timer)) {
+                       q->timer.expires = jiffies + PSCHED_US2JIFFIE(td);
+                       add_timer(&q->timer);
+               }
                return NET_XMIT_SUCCESS;
        }
 
-       sch->stats.drops++;
+       sch->qstats.drops++;
        kfree_skb(skb);
        return NET_XMIT_DROP;
 }
@@ -172,7 +176,8 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
        /* Random packet drop 0 => none, ~0 => all */
        if (q->loss && q->loss >= get_crandom(&q->loss_cor)) {
                pr_debug("netem_enqueue: random loss\n");
-               sch->stats.drops++;
+               sch->qstats.drops++;
+               kfree_skb(skb);
                return 0;       /* lie about loss so TCP doesn't know */
        }
 
@@ -195,8 +200,12 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 
                ++q->counter;
                ret = q->qdisc->enqueue(skb, q->qdisc);
-               if (ret)
-                       sch->stats.drops++;
+               if (likely(ret == NET_XMIT_SUCCESS)) {
+                       sch->q.qlen++;
+                       sch->bstats.bytes += skb->len;
+                       sch->bstats.packets++;
+               } else
+                       sch->qstats.drops++;
                return ret;
        }
        
@@ -211,8 +220,10 @@ static int netem_requeue(struct sk_buff *skb, struct Qdisc *sch)
        struct netem_sched_data *q = qdisc_priv(sch);
        int ret;
 
-       if ((ret = q->qdisc->ops->requeue(skb, q->qdisc)) == 0)
+       if ((ret = q->qdisc->ops->requeue(skb, q->qdisc)) == 0) {
                sch->q.qlen++;
+               sch->qstats.requeues++;
+       }
 
        return ret;
 }
@@ -224,7 +235,7 @@ static unsigned int netem_drop(struct Qdisc* sch)
 
        if ((len = q->qdisc->ops->drop(q->qdisc)) != 0) {
                sch->q.qlen--;
-               sch->stats.drops++;
+               sch->qstats.drops++;
        }
        return len;
 }
@@ -237,15 +248,32 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch)
 {
        struct netem_sched_data *q = qdisc_priv(sch);
        struct sk_buff *skb;
+
+       skb = q->qdisc->dequeue(q->qdisc);
+       if (skb) 
+               sch->q.qlen--;
+       return skb;
+}
+
+static void netem_watchdog(unsigned long arg)
+{
+       struct Qdisc *sch = (struct Qdisc *)arg;
+       struct netem_sched_data *q = qdisc_priv(sch);
+       struct net_device *dev = sch->dev;
+       struct sk_buff *skb;
        psched_time_t now;
 
+       pr_debug("netem_watchdog: fired @%lu\n", jiffies);
+
+       spin_lock_bh(&dev->queue_lock);
        PSCHED_GET_TIME(now);
+
        while ((skb = skb_peek(&q->delayed)) != NULL) {
                const struct netem_skb_cb *cb
                        = (const struct netem_skb_cb *)skb->cb;
                long delay 
                        = PSCHED_US2JIFFIE(PSCHED_TDIFF(cb->time_to_send, now));
-               pr_debug("netem_dequeue: delay queue %p@%lu %ld\n",
+               pr_debug("netem_watchdog: skb %p@%lu %ld\n",
                         skb, jiffies, delay);
 
                /* if more time remaining? */
@@ -256,21 +284,12 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch)
                __skb_unlink(skb, &q->delayed);
 
                if (q->qdisc->enqueue(skb, q->qdisc))
-                       sch->stats.drops++;
+                       sch->qstats.drops++;
+               else
+                       sch->q.qlen++;
        }
-
-       skb = q->qdisc->dequeue(q->qdisc);
-       if (skb) 
-               sch->q.qlen--;
-       return skb;
-}
-
-static void netem_watchdog(unsigned long arg)
-{
-       struct Qdisc *sch = (struct Qdisc *)arg;
-
-       pr_debug("netem_watchdog: fired @%lu\n", jiffies);
-       netif_schedule(sch->dev);
+       qdisc_run(dev);
+       spin_unlock_bh(&dev->queue_lock);
 }
 
 static void netem_reset(struct Qdisc *sch)