]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - net/sched/sch_choke.c
Merge tag 'for-linus-3.3' of git://git.infradead.org/mtd-2.6
[mv-sheeva.git] / net / sched / sch_choke.c
index 3422b25df9e46d8149ea0591ddc1625740b64bb6..e465064d39a369aad077f8ed55a54fd1f1cac31d 100644 (file)
 #include <net/pkt_sched.h>
 #include <net/inet_ecn.h>
 #include <net/red.h>
-#include <linux/ip.h>
-#include <net/ip.h>
-#include <linux/ipv6.h>
-#include <net/ipv6.h>
+#include <net/flow_keys.h>
 
 /*
    CHOKe stateless AQM for fair bandwidth allocation
@@ -60,6 +57,7 @@ struct choke_sched_data {
        struct red_parms parms;
 
 /* Variables */
+       struct red_vars  vars;
        struct tcf_proto *filter_list;
        struct {
                u32     prob_drop;      /* Early probability drops */
@@ -142,85 +140,10 @@ static void choke_drop_by_idx(struct Qdisc *sch, unsigned int idx)
        --sch->q.qlen;
 }
 
-/*
- * Compare flow of two packets
- *  Returns true only if source and destination address and port match.
- *          false for special cases
- */
-static bool choke_match_flow(struct sk_buff *skb1,
-                            struct sk_buff *skb2)
-{
-       int off1, off2, poff;
-       const u32 *ports1, *ports2;
-       u8 ip_proto;
-       __u32 hash1;
-
-       if (skb1->protocol != skb2->protocol)
-               return false;
-
-       /* Use hash value as quick check
-        * Assumes that __skb_get_rxhash makes IP header and ports linear
-        */
-       hash1 = skb_get_rxhash(skb1);
-       if (!hash1 || hash1 != skb_get_rxhash(skb2))
-               return false;
-
-       /* Probably match, but be sure to avoid hash collisions */
-       off1 = skb_network_offset(skb1);
-       off2 = skb_network_offset(skb2);
-
-       switch (skb1->protocol) {
-       case __constant_htons(ETH_P_IP): {
-               const struct iphdr *ip1, *ip2;
-
-               ip1 = (const struct iphdr *) (skb1->data + off1);
-               ip2 = (const struct iphdr *) (skb2->data + off2);
-
-               ip_proto = ip1->protocol;
-               if (ip_proto != ip2->protocol ||
-                   ip1->saddr != ip2->saddr || ip1->daddr != ip2->daddr)
-                       return false;
-
-               if (ip_is_fragment(ip1) | ip_is_fragment(ip2))
-                       ip_proto = 0;
-               off1 += ip1->ihl * 4;
-               off2 += ip2->ihl * 4;
-               break;
-       }
-
-       case __constant_htons(ETH_P_IPV6): {
-               const struct ipv6hdr *ip1, *ip2;
-
-               ip1 = (const struct ipv6hdr *) (skb1->data + off1);
-               ip2 = (const struct ipv6hdr *) (skb2->data + off2);
-
-               ip_proto = ip1->nexthdr;
-               if (ip_proto != ip2->nexthdr ||
-                   ipv6_addr_cmp(&ip1->saddr, &ip2->saddr) ||
-                   ipv6_addr_cmp(&ip1->daddr, &ip2->daddr))
-                       return false;
-               off1 += 40;
-               off2 += 40;
-       }
-
-       default: /* Maybe compare MAC header here? */
-               return false;
-       }
-
-       poff = proto_ports_offset(ip_proto);
-       if (poff < 0)
-               return true;
-
-       off1 += poff;
-       off2 += poff;
-
-       ports1 = (__force u32 *)(skb1->data + off1);
-       ports2 = (__force u32 *)(skb2->data + off2);
-       return *ports1 == *ports2;
-}
-
 struct choke_skb_cb {
-       u16 classid;
+       u16                     classid;
+       u8                      keys_valid;
+       struct flow_keys        keys;
 };
 
 static inline struct choke_skb_cb *choke_skb_cb(const struct sk_buff *skb)
@@ -240,6 +163,32 @@ static u16 choke_get_classid(const struct sk_buff *skb)
        return choke_skb_cb(skb)->classid;
 }
 
+/*
+ * Compare flow of two packets
+ *  Returns true only if source and destination address and port match.
+ *          false for special cases
+ */
+static bool choke_match_flow(struct sk_buff *skb1,
+                            struct sk_buff *skb2)
+{
+       if (skb1->protocol != skb2->protocol)
+               return false;
+
+       if (!choke_skb_cb(skb1)->keys_valid) {
+               choke_skb_cb(skb1)->keys_valid = 1;
+               skb_flow_dissect(skb1, &choke_skb_cb(skb1)->keys);
+       }
+
+       if (!choke_skb_cb(skb2)->keys_valid) {
+               choke_skb_cb(skb2)->keys_valid = 1;
+               skb_flow_dissect(skb2, &choke_skb_cb(skb2)->keys);
+       }
+
+       return !memcmp(&choke_skb_cb(skb1)->keys,
+                      &choke_skb_cb(skb2)->keys,
+                      sizeof(struct flow_keys));
+}
+
 /*
  * Classify flow using either:
  *  1. pre-existing classification result in skb
@@ -317,7 +266,7 @@ static bool choke_match_random(const struct choke_sched_data *q,
 static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 {
        struct choke_sched_data *q = qdisc_priv(sch);
-       struct red_parms *p = &q->parms;
+       const struct red_parms *p = &q->parms;
        int ret = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
 
        if (q->filter_list) {
@@ -326,14 +275,15 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
                        goto other_drop;        /* Packet was eaten by filter */
        }
 
+       choke_skb_cb(skb)->keys_valid = 0;
        /* Compute average queue usage (see RED) */
-       p->qavg = red_calc_qavg(p, sch->q.qlen);
-       if (red_is_idling(p))
-               red_end_of_idle_period(p);
+       q->vars.qavg = red_calc_qavg(p, &q->vars, sch->q.qlen);
+       if (red_is_idling(&q->vars))
+               red_end_of_idle_period(&q->vars);
 
        /* Is queue small? */
-       if (p->qavg <= p->qth_min)
-               p->qcount = -1;
+       if (q->vars.qavg <= p->qth_min)
+               q->vars.qcount = -1;
        else {
                unsigned int idx;
 
@@ -345,8 +295,8 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
                }
 
                /* Queue is large, always mark/drop */
-               if (p->qavg > p->qth_max) {
-                       p->qcount = -1;
+               if (q->vars.qavg > p->qth_max) {
+                       q->vars.qcount = -1;
 
                        sch->qstats.overlimits++;
                        if (use_harddrop(q) || !use_ecn(q) ||
@@ -356,10 +306,10 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
                        }
 
                        q->stats.forced_mark++;
-               } else if (++p->qcount) {
-                       if (red_mark_probability(p, p->qavg)) {
-                               p->qcount = 0;
-                               p->qR = red_random(p);
+               } else if (++q->vars.qcount) {
+                       if (red_mark_probability(p, &q->vars, q->vars.qavg)) {
+                               q->vars.qcount = 0;
+                               q->vars.qR = red_random(p);
 
                                sch->qstats.overlimits++;
                                if (!use_ecn(q) || !INET_ECN_set_ce(skb)) {
@@ -370,7 +320,7 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
                                q->stats.prob_mark++;
                        }
                } else
-                       p->qR = red_random(p);
+                       q->vars.qR = red_random(p);
        }
 
        /* Admit new packet */
@@ -404,8 +354,8 @@ static struct sk_buff *choke_dequeue(struct Qdisc *sch)
        struct sk_buff *skb;
 
        if (q->head == q->tail) {
-               if (!red_is_idling(&q->parms))
-                       red_start_of_idle_period(&q->parms);
+               if (!red_is_idling(&q->vars))
+                       red_start_of_idle_period(&q->vars);
                return NULL;
        }
 
@@ -428,8 +378,8 @@ static unsigned int choke_drop(struct Qdisc *sch)
        if (len > 0)
                q->stats.other++;
        else {
-               if (!red_is_idling(&q->parms))
-                       red_start_of_idle_period(&q->parms);
+               if (!red_is_idling(&q->vars))
+                       red_start_of_idle_period(&q->vars);
        }
 
        return len;
@@ -439,12 +389,13 @@ static void choke_reset(struct Qdisc *sch)
 {
        struct choke_sched_data *q = qdisc_priv(sch);
 
-       red_restart(&q->parms);
+       red_restart(&q->vars);
 }
 
 static const struct nla_policy choke_policy[TCA_CHOKE_MAX + 1] = {
        [TCA_CHOKE_PARMS]       = { .len = sizeof(struct tc_red_qopt) },
        [TCA_CHOKE_STAB]        = { .len = RED_STAB_SIZE },
+       [TCA_CHOKE_MAX_P]       = { .type = NLA_U32 },
 };
 
 
@@ -466,6 +417,7 @@ static int choke_change(struct Qdisc *sch, struct nlattr *opt)
        int err;
        struct sk_buff **old = NULL;
        unsigned int mask;
+       u32 max_P;
 
        if (opt == NULL)
                return -EINVAL;
@@ -478,6 +430,8 @@ static int choke_change(struct Qdisc *sch, struct nlattr *opt)
            tb[TCA_CHOKE_STAB] == NULL)
                return -EINVAL;
 
+       max_P = tb[TCA_CHOKE_MAX_P] ? nla_get_u32(tb[TCA_CHOKE_MAX_P]) : 0;
+
        ctl = nla_data(tb[TCA_CHOKE_PARMS]);
 
        if (ctl->limit > CHOKE_MAX_QUEUE)
@@ -527,10 +481,12 @@ static int choke_change(struct Qdisc *sch, struct nlattr *opt)
 
        red_set_parms(&q->parms, ctl->qth_min, ctl->qth_max, ctl->Wlog,
                      ctl->Plog, ctl->Scell_log,
-                     nla_data(tb[TCA_CHOKE_STAB]));
+                     nla_data(tb[TCA_CHOKE_STAB]),
+                     max_P);
+       red_set_vars(&q->vars);
 
        if (q->head == q->tail)
-               red_end_of_idle_period(&q->parms);
+               red_end_of_idle_period(&q->vars);
 
        sch_tree_unlock(sch);
        choke_free(old);
@@ -561,6 +517,7 @@ static int choke_dump(struct Qdisc *sch, struct sk_buff *skb)
                goto nla_put_failure;
 
        NLA_PUT(skb, TCA_CHOKE_PARMS, sizeof(opt), &opt);
+       NLA_PUT_U32(skb, TCA_CHOKE_MAX_P, q->parms.max_P);
        return nla_nest_end(skb, opts);
 
 nla_put_failure: