]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/sched/sch_netem.c
Merge branch 'upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/vitb/linux...
[linux-2.6-omap-h63xx.git] / net / sched / sch_netem.c
index cdc8d283791c7d89388d911cf76c344e0ad04ba9..45939bafbdf894dfcb61a0ed993d1d79204aa5a8 100644 (file)
@@ -13,7 +13,6 @@
  *             Catalin(ux aka Dino) BOIE <catab at umbrella dot ro>
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/bitops.h>
 #include <linux/types.h>
@@ -25,7 +24,7 @@
 
 #include <net/pkt_sched.h>
 
-#define VERSION "1.1"
+#define VERSION "1.2"
 
 /*     Network Emulation Queuing algorithm.
        ====================================
@@ -65,11 +64,12 @@ struct netem_sched_data {
        u32 jitter;
        u32 duplicate;
        u32 reorder;
+       u32 corrupt;
 
        struct crndstate {
                unsigned long last;
                unsigned long rho;
-       } delay_cor, loss_cor, dup_cor, reorder_cor;
+       } delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor;
 
        struct disttable {
                u32  size;
@@ -148,7 +148,8 @@ static long tabledist(unsigned long mu, long sigma,
 static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 {
        struct netem_sched_data *q = qdisc_priv(sch);
-       struct netem_skb_cb *cb = (struct netem_skb_cb *)skb->cb;
+       /* We don't fill cb now as skb_unshare() may invalidate it */
+       struct netem_skb_cb *cb;
        struct sk_buff *skb2;
        int ret;
        int count = 1;
@@ -166,7 +167,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
        if (count == 0) {
                sch->qstats.drops++;
                kfree_skb(skb);
-               return NET_XMIT_DROP;
+               return NET_XMIT_BYPASS;
        }
 
        /*
@@ -183,6 +184,24 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
                q->duplicate = dupsave;
        }
 
+       /*
+        * Randomized packet corruption.
+        * Make copy if needed since we are modifying
+        * If packet is going to be hardware checksummed, then
+        * do it now in software before we mangle it.
+        */
+       if (q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor)) {
+               if (!(skb = skb_unshare(skb, GFP_ATOMIC))
+                   || (skb->ip_summed == CHECKSUM_PARTIAL
+                       && skb_checksum_help(skb))) {
+                       sch->qstats.drops++;
+                       return NET_XMIT_DROP;
+               }
+
+               skb->data[net_random() % skb_headlen(skb)] ^= 1<<(net_random() % 8);
+       }
+
+       cb = (struct netem_skb_cb *)skb->cb;
        if (q->gap == 0                 /* not doing reordering */
            || q->counter < q->gap      /* inside last reordering gap */
            || q->reorder < get_crandom(&q->reorder_cor)) {
@@ -234,9 +253,9 @@ static int netem_requeue(struct sk_buff *skb, struct Qdisc *sch)
 static unsigned int netem_drop(struct Qdisc* sch)
 {
        struct netem_sched_data *q = qdisc_priv(sch);
-       unsigned int len;
+       unsigned int len = 0;
 
-       if ((len = q->qdisc->ops->drop(q->qdisc)) != 0) {
+       if (q->qdisc->ops->drop && (len = q->qdisc->ops->drop(q->qdisc)) != 0) {
                sch->q.qlen--;
                sch->qstats.drops++;
        }
@@ -382,6 +401,20 @@ static int get_reorder(struct Qdisc *sch, const struct rtattr *attr)
        return 0;
 }
 
+static int get_corrupt(struct Qdisc *sch, const struct rtattr *attr)
+{
+       struct netem_sched_data *q = qdisc_priv(sch);
+       const struct tc_netem_corrupt *r = RTA_DATA(attr);
+
+       if (RTA_PAYLOAD(attr) != sizeof(*r))
+               return -EINVAL;
+
+       q->corrupt = r->probability;
+       init_crandom(&q->corrupt_cor, r->correlation);
+       return 0;
+}
+
+/* Parse netlink message to set options */
 static int netem_change(struct Qdisc *sch, struct rtattr *opt)
 {
        struct netem_sched_data *q = qdisc_priv(sch);
@@ -432,13 +465,19 @@ static int netem_change(struct Qdisc *sch, struct rtattr *opt)
                        if (ret)
                                return ret;
                }
+
                if (tb[TCA_NETEM_REORDER-1]) {
                        ret = get_reorder(sch, tb[TCA_NETEM_REORDER-1]);
                        if (ret)
                                return ret;
                }
-       }
 
+               if (tb[TCA_NETEM_CORRUPT-1]) {
+                       ret = get_corrupt(sch, tb[TCA_NETEM_CORRUPT-1]);
+                       if (ret)
+                               return ret;
+               }
+       }
 
        return 0;
 }
@@ -464,7 +503,7 @@ static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
                        const struct netem_skb_cb *cb
                                = (const struct netem_skb_cb *)skb->cb;
 
-                       if (PSCHED_TLESS(cb->time_to_send, ncb->time_to_send))
+                       if (!PSCHED_TLESS(ncb->time_to_send, cb->time_to_send))
                                break;
                }
 
@@ -564,6 +603,7 @@ static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
        struct tc_netem_qopt qopt;
        struct tc_netem_corr cor;
        struct tc_netem_reorder reorder;
+       struct tc_netem_corrupt corrupt;
 
        qopt.latency = q->latency;
        qopt.jitter = q->jitter;
@@ -582,6 +622,10 @@ static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
        reorder.correlation = q->reorder_cor.rho;
        RTA_PUT(skb, TCA_NETEM_REORDER, sizeof(reorder), &reorder);
 
+       corrupt.probability = q->corrupt;
+       corrupt.correlation = q->corrupt_cor.rho;
+       RTA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
+
        rta->rta_len = skb->tail - b;
 
        return skb->len;