b43: Remove unnecessary MMIO accesses in the interrupt hotpath.
[openwrt.git] / target / linux / generic-2.6 / patches-2.6.23 / 200-sched_esfq.patch
index 4e626c5..06c9c7b 100644 (file)
@@ -1,13 +1,11 @@
-Index: linux-2.6.23-rc6/include/linux/pkt_sched.h
-===================================================================
---- linux-2.6.23-rc6.orig/include/linux/pkt_sched.h    2007-09-21 16:23:53.000000000 +0800
-+++ linux-2.6.23-rc6/include/linux/pkt_sched.h 2007-09-21 16:24:04.000000000 +0800
-@@ -155,8 +155,40 @@
+--- a/include/linux/pkt_sched.h
++++ b/include/linux/pkt_sched.h
+@@ -155,8 +155,37 @@ struct tc_sfq_qopt
   *
   *    The only reason for this is efficiency, it is possible
   *    to change these parameters in compile time.
   *
   *    The only reason for this is efficiency, it is possible
   *    to change these parameters in compile time.
-+ *
-+ *    If you need to play with these values use esfq instead.
++ *    
++ *    If you need to play with these values, use esfq instead.
   */
  
 +/* ESFQ section */
   */
  
 +/* ESFQ section */
@@ -19,15 +17,12 @@ Index: linux-2.6.23-rc6/include/linux/pkt_sched.h
 +      TCA_SFQ_HASH_DST,
 +      TCA_SFQ_HASH_SRC,
 +      TCA_SFQ_HASH_FWMARK,
 +      TCA_SFQ_HASH_DST,
 +      TCA_SFQ_HASH_SRC,
 +      TCA_SFQ_HASH_FWMARK,
-+        /* direct */
-+      TCA_SFQ_HASH_DSTDIR,
-+      TCA_SFQ_HASH_SRCDIR,
-+      TCA_SFQ_HASH_FWMARKDIR,
 +      /* conntrack */
 +      TCA_SFQ_HASH_CTORIGDST,
 +      TCA_SFQ_HASH_CTORIGSRC,
 +      TCA_SFQ_HASH_CTREPLDST,
 +      TCA_SFQ_HASH_CTREPLSRC,
 +      /* conntrack */
 +      TCA_SFQ_HASH_CTORIGDST,
 +      TCA_SFQ_HASH_CTORIGSRC,
 +      TCA_SFQ_HASH_CTREPLDST,
 +      TCA_SFQ_HASH_CTREPLSRC,
++      TCA_SFQ_HASH_CTNATCHG,
 +};
 +
 +struct tc_esfq_qopt
 +};
 +
 +struct tc_esfq_qopt
@@ -43,11 +38,9 @@ Index: linux-2.6.23-rc6/include/linux/pkt_sched.h
  /* RED section */
  
  enum
  /* RED section */
  
  enum
-Index: linux-2.6.23-rc6/net/sched/Kconfig
-===================================================================
---- linux-2.6.23-rc6.orig/net/sched/Kconfig    2007-09-21 16:23:53.000000000 +0800
-+++ linux-2.6.23-rc6/net/sched/Kconfig 2007-09-21 16:24:04.000000000 +0800
-@@ -144,6 +144,26 @@
+--- a/net/sched/Kconfig
++++ b/net/sched/Kconfig
+@@ -144,6 +144,37 @@ config NET_SCH_SFQ
          To compile this code as a module, choose M here: the
          module will be called sch_sfq.
  
          To compile this code as a module, choose M here: the
          module will be called sch_sfq.
  
@@ -67,18 +60,27 @@ Index: linux-2.6.23-rc6/net/sched/Kconfig
 +        flows. The original SFQ discipline hashes by connection; ESFQ add
 +        several other hashing methods, such as by src IP or by dst IP, which
 +        can be more fair to users in some networking situations.
 +        flows. The original SFQ discipline hashes by connection; ESFQ add
 +        several other hashing methods, such as by src IP or by dst IP, which
 +        can be more fair to users in some networking situations.
-+
++        
 +        To compile this code as a module, choose M here: the
 +        module will be called sch_esfq.
 +        To compile this code as a module, choose M here: the
 +        module will be called sch_esfq.
++
++config NET_SCH_ESFQ_NFCT
++      bool "Connection Tracking Hash Types"
++      depends on NET_SCH_ESFQ && NF_CONNTRACK
++      ---help---
++        Say Y here to enable support for hashing based on netfilter connection
++        tracking information. This is useful for a router that is also using
++        NAT to connect privately-addressed hosts to the Internet. If you want
++        to provide fair distribution of upstream bandwidth, ESFQ must use
++        connection tracking information, since all outgoing packets will share
++        the same source address.
 +
  config NET_SCH_TEQL
        tristate "True Link Equalizer (TEQL)"
        ---help---
 +
  config NET_SCH_TEQL
        tristate "True Link Equalizer (TEQL)"
        ---help---
-Index: linux-2.6.23-rc6/net/sched/Makefile
-===================================================================
---- linux-2.6.23-rc6.orig/net/sched/Makefile   2007-09-21 16:23:53.000000000 +0800
-+++ linux-2.6.23-rc6/net/sched/Makefile        2007-09-21 16:24:04.000000000 +0800
-@@ -22,6 +22,7 @@
+--- a/net/sched/Makefile
++++ b/net/sched/Makefile
+@@ -22,6 +22,7 @@ obj-$(CONFIG_NET_SCH_GRED)   += sch_gred.o
  obj-$(CONFIG_NET_SCH_INGRESS) += sch_ingress.o 
  obj-$(CONFIG_NET_SCH_DSMARK)  += sch_dsmark.o
  obj-$(CONFIG_NET_SCH_SFQ)     += sch_sfq.o
  obj-$(CONFIG_NET_SCH_INGRESS) += sch_ingress.o 
  obj-$(CONFIG_NET_SCH_DSMARK)  += sch_dsmark.o
  obj-$(CONFIG_NET_SCH_SFQ)     += sch_sfq.o
@@ -86,11 +88,9 @@ Index: linux-2.6.23-rc6/net/sched/Makefile
  obj-$(CONFIG_NET_SCH_TBF)     += sch_tbf.o
  obj-$(CONFIG_NET_SCH_TEQL)    += sch_teql.o
  obj-$(CONFIG_NET_SCH_PRIO)    += sch_prio.o
  obj-$(CONFIG_NET_SCH_TBF)     += sch_tbf.o
  obj-$(CONFIG_NET_SCH_TEQL)    += sch_teql.o
  obj-$(CONFIG_NET_SCH_PRIO)    += sch_prio.o
-Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
-===================================================================
---- /dev/null  1970-01-01 00:00:00.000000000 +0000
-+++ linux-2.6.23-rc6/net/sched/sch_esfq.c      2007-09-21 16:24:04.000000000 +0800
-@@ -0,0 +1,704 @@
+--- /dev/null
++++ b/net/sched/sch_esfq.c
+@@ -0,0 +1,702 @@
 +/*
 + * net/sched/sch_esfq.c       Extended Stochastic Fairness Queueing discipline.
 + *
 +/*
 + * net/sched/sch_esfq.c       Extended Stochastic Fairness Queueing discipline.
 + *
@@ -111,9 +111,9 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 + *            Corey Hickey, <bugfood-c@fatooh.org>
 + *            Maintenance of the Linux 2.6 port.
 + *            Added fwmark hash (thanks to Robert Kurjata).
 + *            Corey Hickey, <bugfood-c@fatooh.org>
 + *            Maintenance of the Linux 2.6 port.
 + *            Added fwmark hash (thanks to Robert Kurjata).
-+ *            Added direct hashing for src, dst, and fwmark.
 + *            Added usage of jhash.
 + *            Added usage of jhash.
-+ *
++ *            Added conntrack support.
++ *            Added ctnatchg hash (thanks to Ben Pfountz).
 + */
 +
 +#include <linux/module.h>
 + */
 +
 +#include <linux/module.h>
@@ -143,30 +143,26 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +#include <net/sock.h>
 +#include <net/pkt_sched.h>
 +#include <linux/jhash.h>
 +#include <net/sock.h>
 +#include <net/pkt_sched.h>
 +#include <linux/jhash.h>
-+
-+#ifdef CONFIG_NF_CONNTRACK_ENABLED
 +#include <net/netfilter/nf_conntrack.h>
 +#include <net/netfilter/nf_conntrack.h>
-+#endif
 +
 +/*    Stochastic Fairness Queuing algorithm.
 +      For more comments look at sch_sfq.c.
 +      The difference is that you can change limit, depth,
 +      hash table size and choose alternate hash types.
 +
 +/*    Stochastic Fairness Queuing algorithm.
 +      For more comments look at sch_sfq.c.
 +      The difference is that you can change limit, depth,
 +      hash table size and choose alternate hash types.
-+
++      
 +      classic:        same as in sch_sfq.c
 +      dst:            destination IP address
 +      src:            source IP address
 +      classic:        same as in sch_sfq.c
 +      dst:            destination IP address
 +      src:            source IP address
-+      fwmark:         netfilter mark value
-+      dst_direct:
-+      src_direct:
-+      fwmark_direct:  direct hashing of the above sources
++      fwmark:         netfilter mark value
 +      ctorigdst:      original destination IP address
 +      ctorigsrc:      original source IP address
 +      ctrepldst:      reply destination IP address
 +      ctorigdst:      original destination IP address
 +      ctorigsrc:      original source IP address
 +      ctrepldst:      reply destination IP address
-+      ctreplsrc:      reply source IP
-+
++      ctreplsrc:      reply source IP 
++      
 +*/
 +
 +*/
 +
++#define ESFQ_HEAD 0
++#define ESFQ_TAIL 1
 +
 +/* This type should contain at least SFQ_DEPTH*2 values */
 +typedef unsigned int esfq_index;
 +
 +/* This type should contain at least SFQ_DEPTH*2 values */
 +typedef unsigned int esfq_index;
@@ -198,9 +194,6 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +      unsigned short  *hash;                  /* Hash value indexed by slots */
 +      struct sk_buff_head     *qs;            /* Slot queue */
 +      struct esfq_head        *dep;           /* Linked list of slots, indexed by depth */
 +      unsigned short  *hash;                  /* Hash value indexed by slots */
 +      struct sk_buff_head     *qs;            /* Slot queue */
 +      struct esfq_head        *dep;           /* Linked list of slots, indexed by depth */
-+      unsigned        dyn_min;        /* For dynamic divisor adjustment; minimum value seen */
-+      unsigned        dyn_max;        /*                                 maximum value seen */
-+      unsigned        dyn_range;      /*                                 saved range */
 +};
 +
 +/* This contains the info we will hash. */
 +};
 +
 +/* This contains the info we will hash. */
@@ -216,31 +209,6 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +      u32     mark;           /* netfilter mark (fwmark) */
 +};
 +
 +      u32     mark;           /* netfilter mark (fwmark) */
 +};
 +
-+/* Hash input values directly into the "nearest" slot, taking into account the
-+ * range of input values seen. This is most useful when the hash table is at
-+ * least as large as the range of possible values.
-+ * Note: this functionality was added before the change to using jhash, and may
-+ * no longer be useful. */
-+static __inline__ unsigned esfq_hash_direct(struct esfq_sched_data *q, u32 h)
-+{
-+      /* adjust minimum and maximum */
-+      if (h < q->dyn_min || h > q->dyn_max) {
-+              q->dyn_min = h < q->dyn_min ? h : q->dyn_min;
-+              q->dyn_max = h > q->dyn_max ? h : q->dyn_max;
-+
-+              /* find new range */
-+              if ((q->dyn_range = q->dyn_max - q->dyn_min) >= q->hash_divisor)
-+                      printk(KERN_WARNING "ESFQ: (direct hash) Input range %u is larger than hash "
-+                                      "table. See ESFQ README for details.\n", q->dyn_range);
-+      }
-+
-+      /* hash input values into slot numbers */
-+      if (q->dyn_min == q->dyn_max)
-+              return 0; /* only one value seen; avoid division by 0 */
-+      else
-+              return (h - q->dyn_min) * (q->hash_divisor - 1) / q->dyn_range;
-+}
-+
 +static __inline__ unsigned esfq_jhash_1word(struct esfq_sched_data *q,u32 a)
 +{
 +      return jhash_1word(a, q->perturbation) & (q->hash_divisor-1);
 +static __inline__ unsigned esfq_jhash_1word(struct esfq_sched_data *q,u32 a)
 +{
 +      return jhash_1word(a, q->perturbation) & (q->hash_divisor-1);
@@ -256,15 +224,14 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +      return jhash_3words(a, b, c, q->perturbation) & (q->hash_divisor-1);
 +}
 +
 +      return jhash_3words(a, b, c, q->perturbation) & (q->hash_divisor-1);
 +}
 +
-+
 +static unsigned esfq_hash(struct esfq_sched_data *q, struct sk_buff *skb)
 +{
 +      struct esfq_packet_info info;
 +static unsigned esfq_hash(struct esfq_sched_data *q, struct sk_buff *skb)
 +{
 +      struct esfq_packet_info info;
-+#ifdef CONFIG_NF_CONNTRACK_ENABLED
++#ifdef CONFIG_NET_SCH_ESFQ_NFCT
 +      enum ip_conntrack_info ctinfo;
 +      struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
 +#endif
 +      enum ip_conntrack_info ctinfo;
 +      struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
 +#endif
-+
++      
 +      switch (skb->protocol) {
 +      case __constant_htons(ETH_P_IP):
 +      {
 +      switch (skb->protocol) {
 +      case __constant_htons(ETH_P_IP):
 +      {
@@ -307,7 +274,7 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +
 +      info.mark = skb->mark;
 +
 +
 +      info.mark = skb->mark;
 +
-+#ifdef CONFIG_NF_CONNTRACK_ENABLED
++#ifdef CONFIG_NET_SCH_ESFQ_NFCT
 +      /* defaults if there is no conntrack info */
 +      info.ctorigsrc = info.src;
 +      info.ctorigdst = info.dst;
 +      /* defaults if there is no conntrack info */
 +      info.ctorigsrc = info.src;
 +      info.ctorigdst = info.dst;
@@ -332,23 +299,16 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +      }
 +#endif
 +
 +      }
 +#endif
 +
-+      switch(q->hash_kind)
-+      {
++      switch(q->hash_kind) {
 +      case TCA_SFQ_HASH_CLASSIC:
 +              return esfq_jhash_3words(q, info.dst, info.src, info.proto);
 +      case TCA_SFQ_HASH_DST:
 +              return esfq_jhash_1word(q, info.dst);
 +      case TCA_SFQ_HASH_CLASSIC:
 +              return esfq_jhash_3words(q, info.dst, info.src, info.proto);
 +      case TCA_SFQ_HASH_DST:
 +              return esfq_jhash_1word(q, info.dst);
-+      case TCA_SFQ_HASH_DSTDIR:
-+              return esfq_hash_direct(q, ntohl(info.dst));
 +      case TCA_SFQ_HASH_SRC:
 +              return esfq_jhash_1word(q, info.src);
 +      case TCA_SFQ_HASH_SRC:
 +              return esfq_jhash_1word(q, info.src);
-+      case TCA_SFQ_HASH_SRCDIR:
-+              return esfq_hash_direct(q, ntohl(info.src));
 +      case TCA_SFQ_HASH_FWMARK:
 +              return esfq_jhash_1word(q, info.mark);
 +      case TCA_SFQ_HASH_FWMARK:
 +              return esfq_jhash_1word(q, info.mark);
-+      case TCA_SFQ_HASH_FWMARKDIR:
-+              return esfq_hash_direct(q, info.mark);
-+#ifdef CONFIG_NF_CONNTRACK_ENABLED
++#ifdef CONFIG_NET_SCH_ESFQ_NFCT
 +      case TCA_SFQ_HASH_CTORIGDST:
 +              return esfq_jhash_1word(q, info.ctorigdst);
 +      case TCA_SFQ_HASH_CTORIGSRC:
 +      case TCA_SFQ_HASH_CTORIGDST:
 +              return esfq_jhash_1word(q, info.ctorigdst);
 +      case TCA_SFQ_HASH_CTORIGSRC:
@@ -357,6 +317,12 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +              return esfq_jhash_1word(q, info.ctrepldst);
 +      case TCA_SFQ_HASH_CTREPLSRC:
 +              return esfq_jhash_1word(q, info.ctreplsrc);
 +              return esfq_jhash_1word(q, info.ctrepldst);
 +      case TCA_SFQ_HASH_CTREPLSRC:
 +              return esfq_jhash_1word(q, info.ctreplsrc);
++      case TCA_SFQ_HASH_CTNATCHG:
++      {
++              if (info.ctorigdst == info.ctreplsrc)
++                      return esfq_jhash_1word(q, info.ctorigsrc);
++              return esfq_jhash_1word(q, info.ctreplsrc);
++      }
 +#endif
 +      default:
 +              if (net_ratelimit())
 +#endif
 +      default:
 +              if (net_ratelimit())
@@ -451,10 +417,8 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +      return 0;
 +}
 +
 +      return 0;
 +}
 +
-+static int
-+esfq_enqueue(struct sk_buff *skb, struct Qdisc* sch)
++static void esfq_q_enqueue(struct sk_buff *skb, struct esfq_sched_data *q, unsigned int end)
 +{
 +{
-+      struct esfq_sched_data *q = qdisc_priv(sch);
 +      unsigned hash = esfq_hash(q, skb);
 +      unsigned depth = q->depth;
 +      esfq_index x;
 +      unsigned hash = esfq_hash(q, skb);
 +      unsigned depth = q->depth;
 +      esfq_index x;
@@ -464,8 +428,12 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +              q->ht[hash] = x = q->dep[depth].next;
 +              q->hash[x] = hash;
 +      }
 +              q->ht[hash] = x = q->dep[depth].next;
 +              q->hash[x] = hash;
 +      }
-+      sch->qstats.backlog += skb->len;
-+      __skb_queue_tail(&q->qs[x], skb);
++
++      if (end == ESFQ_TAIL)
++              __skb_queue_tail(&q->qs[x], skb);
++      else
++              __skb_queue_head(&q->qs[x], skb);
++
 +      esfq_inc(q, x);
 +      if (q->qs[x].qlen == 1) {               /* The flow is new */
 +              if (q->tail == depth) { /* It is the first flow */
 +      esfq_inc(q, x);
 +      if (q->qs[x].qlen == 1) {               /* The flow is new */
 +              if (q->tail == depth) { /* It is the first flow */
@@ -478,43 +446,30 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +                      q->tail = x;
 +              }
 +      }
 +                      q->tail = x;
 +              }
 +      }
++}
++
++static int esfq_enqueue(struct sk_buff *skb, struct Qdisc* sch)
++{
++      struct esfq_sched_data *q = qdisc_priv(sch);
++      esfq_q_enqueue(skb, q, ESFQ_TAIL);
++      sch->qstats.backlog += skb->len;
 +      if (++sch->q.qlen < q->limit-1) {
 +              sch->bstats.bytes += skb->len;
 +              sch->bstats.packets++;
 +              return 0;
 +      }
 +
 +      if (++sch->q.qlen < q->limit-1) {
 +              sch->bstats.bytes += skb->len;
 +              sch->bstats.packets++;
 +              return 0;
 +      }
 +
++      sch->qstats.drops++;
 +      esfq_drop(sch);
 +      return NET_XMIT_CN;
 +}
 +
 +      esfq_drop(sch);
 +      return NET_XMIT_CN;
 +}
 +
-+static int
-+esfq_requeue(struct sk_buff *skb, struct Qdisc* sch)
++
++static int esfq_requeue(struct sk_buff *skb, struct Qdisc* sch)
 +{
 +      struct esfq_sched_data *q = qdisc_priv(sch);
 +{
 +      struct esfq_sched_data *q = qdisc_priv(sch);
-+      unsigned hash = esfq_hash(q, skb);
-+      unsigned depth = q->depth;
-+      esfq_index x;
-+
-+      x = q->ht[hash];
-+      if (x == depth) {
-+              q->ht[hash] = x = q->dep[depth].next;
-+              q->hash[x] = hash;
-+      }
++      esfq_q_enqueue(skb, q, ESFQ_HEAD);
 +      sch->qstats.backlog += skb->len;
 +      sch->qstats.backlog += skb->len;
-+      __skb_queue_head(&q->qs[x], skb);
-+      esfq_inc(q, x);
-+      if (q->qs[x].qlen == 1) {               /* The flow is new */
-+              if (q->tail == depth) { /* It is the first flow */
-+                      q->tail = x;
-+                      q->next[x] = x;
-+                      q->allot[x] = q->quantum;
-+              } else {
-+                      q->next[x] = q->next[q->tail];
-+                      q->next[q->tail] = x;
-+                      q->tail = x;
-+              }
-+      }
 +      if (++sch->q.qlen < q->limit - 1) {
 +              sch->qstats.requeues++;
 +              return 0;
 +      if (++sch->q.qlen < q->limit - 1) {
 +              sch->qstats.requeues++;
 +              return 0;
@@ -525,13 +480,8 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +      return NET_XMIT_CN;
 +}
 +
 +      return NET_XMIT_CN;
 +}
 +
-+
-+
-+
-+static struct sk_buff *
-+esfq_dequeue(struct Qdisc* sch)
++static struct sk_buff *esfq_q_dequeue(struct esfq_sched_data *q)
 +{
 +{
-+      struct esfq_sched_data *q = qdisc_priv(sch);
 +      struct sk_buff *skb;
 +      unsigned depth = q->depth;
 +      esfq_index a, old_a;
 +      struct sk_buff *skb;
 +      unsigned depth = q->depth;
 +      esfq_index a, old_a;
@@ -539,15 +489,13 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +      /* No active slots */
 +      if (q->tail == depth)
 +              return NULL;
 +      /* No active slots */
 +      if (q->tail == depth)
 +              return NULL;
-+
++      
 +      a = old_a = q->next[q->tail];
 +      a = old_a = q->next[q->tail];
-+
++      
 +      /* Grab packet */
 +      skb = __skb_dequeue(&q->qs[a]);
 +      esfq_dec(q, a);
 +      /* Grab packet */
 +      skb = __skb_dequeue(&q->qs[a]);
 +      esfq_dec(q, a);
-+      sch->q.qlen--;
-+      sch->qstats.backlog -= skb->len;
-+
++      
 +      /* Is the slot empty? */
 +      if (q->qs[a].qlen == 0) {
 +              q->ht[q->hash[a]] = depth;
 +      /* Is the slot empty? */
 +      if (q->qs[a].qlen == 0) {
 +              q->ht[q->hash[a]] = depth;
@@ -563,12 +511,48 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +              a = q->next[a];
 +              q->allot[a] += q->quantum;
 +      }
 +              a = q->next[a];
 +              q->allot[a] += q->quantum;
 +      }
++      
++      return skb;
++}
++
++static struct sk_buff *esfq_dequeue(struct Qdisc* sch)
++{
++      struct esfq_sched_data *q = qdisc_priv(sch);
++      struct sk_buff *skb;
 +
 +
++      skb = esfq_q_dequeue(q);
++      if (skb == NULL)
++              return NULL;
++      sch->q.qlen--;
++      sch->qstats.backlog -= skb->len;
 +      return skb;
 +}
 +
 +      return skb;
 +}
 +
-+static void
-+esfq_reset(struct Qdisc* sch)
++static void esfq_q_destroy(struct esfq_sched_data *q)
++{
++      del_timer(&q->perturb_timer);
++      if(q->ht)
++              kfree(q->ht);
++      if(q->dep)
++              kfree(q->dep);
++      if(q->next)
++              kfree(q->next);
++      if(q->allot)
++              kfree(q->allot);
++      if(q->hash)
++              kfree(q->hash);
++      if(q->qs)
++              kfree(q->qs);
++}
++
++static void esfq_destroy(struct Qdisc *sch)
++{
++      struct esfq_sched_data *q = qdisc_priv(sch);
++      esfq_q_destroy(q);
++}
++
++
++static void esfq_reset(struct Qdisc* sch)
 +{
 +      struct sk_buff *skb;
 +
 +{
 +      struct sk_buff *skb;
 +
@@ -589,104 +573,80 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +      }
 +}
 +
 +      }
 +}
 +
-+static int esfq_change(struct Qdisc *sch, struct rtattr *opt)
++static unsigned int esfq_check_hash(unsigned int kind)
 +{
 +{
-+      struct esfq_sched_data *q = qdisc_priv(sch);
-+      struct tc_esfq_qopt *ctl = RTA_DATA(opt);
-+      int old_perturb = q->perturb_period;
-+
-+      if (opt->rta_len < RTA_LENGTH(sizeof(*ctl)))
-+              return -EINVAL;
-+
-+      sch_tree_lock(sch);
-+      q->quantum = ctl->quantum ? : psched_mtu(sch->dev);
-+      q->perturb_period = ctl->perturb_period*HZ;
-+//    q->hash_divisor = ctl->divisor;
-+//    q->tail = q->limit = q->depth = ctl->flows;
-+
-+      if (ctl->limit)
-+              q->limit = min_t(u32, ctl->limit, q->depth);
-+
-+      if (ctl->hash_kind) {
-+              q->hash_kind = ctl->hash_kind;
-+              if (q->hash_kind != TCA_SFQ_HASH_CLASSIC)
-+                      q->perturb_period = 0;
++      switch (kind) {
++      case TCA_SFQ_HASH_CTORIGDST:
++      case TCA_SFQ_HASH_CTORIGSRC:
++      case TCA_SFQ_HASH_CTREPLDST:
++      case TCA_SFQ_HASH_CTREPLSRC:
++      case TCA_SFQ_HASH_CTNATCHG:
++#ifndef CONFIG_NET_SCH_ESFQ_NFCT
++      {
++              if (net_ratelimit())
++                      printk(KERN_WARNING "ESFQ: Conntrack hash types disabled in kernel config. Falling back to classic.\n");
++              return TCA_SFQ_HASH_CLASSIC;
++      }
++#endif
++      case TCA_SFQ_HASH_CLASSIC:
++      case TCA_SFQ_HASH_DST:
++      case TCA_SFQ_HASH_SRC:
++      case TCA_SFQ_HASH_FWMARK:
++              return kind;
++      default:
++      {
++              if (net_ratelimit())
++                      printk(KERN_WARNING "ESFQ: Unknown hash type. Falling back to classic.\n");
++              return TCA_SFQ_HASH_CLASSIC;
 +      }
 +      }
-+
-+      // is sch_tree_lock enough to do this ?
-+      while (sch->q.qlen >= q->limit-1)
-+              esfq_drop(sch);
-+
-+      if (old_perturb)
-+              del_timer(&q->perturb_timer);
-+      if (q->perturb_period) {
-+              q->perturb_timer.expires = jiffies + q->perturb_period;
-+              add_timer(&q->perturb_timer);
-+      } else {
-+              q->perturbation = 0;
 +      }
 +      }
-+      sch_tree_unlock(sch);
-+      return 0;
 +}
 +}
-+
-+static int esfq_init(struct Qdisc *sch, struct rtattr *opt)
++      
++static int esfq_q_init(struct esfq_sched_data *q, struct rtattr *opt)
 +{
 +{
-+      struct esfq_sched_data *q = qdisc_priv(sch);
-+      struct tc_esfq_qopt *ctl;
++      struct tc_esfq_qopt *ctl = RTA_DATA(opt);
 +      esfq_index p = ~0U/2;
 +      int i;
 +      esfq_index p = ~0U/2;
 +      int i;
-+
++      
 +      if (opt && opt->rta_len < RTA_LENGTH(sizeof(*ctl)))
 +              return -EINVAL;
 +
 +      if (opt && opt->rta_len < RTA_LENGTH(sizeof(*ctl)))
 +              return -EINVAL;
 +
-+      init_timer(&q->perturb_timer);
-+      q->perturb_timer.data = (unsigned long)sch;
-+      q->perturb_timer.function = esfq_perturbation;
 +      q->perturbation = 0;
 +      q->hash_kind = TCA_SFQ_HASH_CLASSIC;
 +      q->max_depth = 0;
 +      q->perturbation = 0;
 +      q->hash_kind = TCA_SFQ_HASH_CLASSIC;
 +      q->max_depth = 0;
-+      q->dyn_min = ~0U; /* maximum value for this type */
-+      q->dyn_max = 0;  /* dyn_min/dyn_max will be set properly upon first packet */
 +      if (opt == NULL) {
 +      if (opt == NULL) {
-+              q->quantum = psched_mtu(sch->dev);
 +              q->perturb_period = 0;
 +              q->hash_divisor = 1024;
 +              q->tail = q->limit = q->depth = 128;
 +              q->perturb_period = 0;
 +              q->hash_divisor = 1024;
 +              q->tail = q->limit = q->depth = 128;
-+
++              
 +      } else {
 +      } else {
-+              ctl = RTA_DATA(opt);
-+              q->quantum = ctl->quantum ? : psched_mtu(sch->dev);
++              struct tc_esfq_qopt *ctl = RTA_DATA(opt);
++              if (ctl->quantum)
++                      q->quantum = ctl->quantum;
 +              q->perturb_period = ctl->perturb_period*HZ;
 +              q->hash_divisor = ctl->divisor ? : 1024;
 +              q->tail = q->limit = q->depth = ctl->flows ? : 128;
 +              q->perturb_period = ctl->perturb_period*HZ;
 +              q->hash_divisor = ctl->divisor ? : 1024;
 +              q->tail = q->limit = q->depth = ctl->flows ? : 128;
-+
++              
 +              if ( q->depth > p - 1 )
 +                      return -EINVAL;
 +              if ( q->depth > p - 1 )
 +                      return -EINVAL;
-+
++              
 +              if (ctl->limit)
 +                      q->limit = min_t(u32, ctl->limit, q->depth);
 +              if (ctl->limit)
 +                      q->limit = min_t(u32, ctl->limit, q->depth);
-+
++              
 +              if (ctl->hash_kind) {
 +              if (ctl->hash_kind) {
-+                      q->hash_kind = ctl->hash_kind;
-+              }
-+
-+              if (q->perturb_period) {
-+                      q->perturb_timer.expires = jiffies + q->perturb_period;
-+                      add_timer(&q->perturb_timer);
++                      q->hash_kind = esfq_check_hash(ctl->hash_kind);
 +              }
 +      }
 +              }
 +      }
-+
++      
 +      q->ht = kmalloc(q->hash_divisor*sizeof(esfq_index), GFP_KERNEL);
 +      if (!q->ht)
 +              goto err_case;
 +      q->ht = kmalloc(q->hash_divisor*sizeof(esfq_index), GFP_KERNEL);
 +      if (!q->ht)
 +              goto err_case;
-+
 +      q->dep = kmalloc((1+q->depth*2)*sizeof(struct esfq_head), GFP_KERNEL);
 +      if (!q->dep)
 +              goto err_case;
 +      q->next = kmalloc(q->depth*sizeof(esfq_index), GFP_KERNEL);
 +      if (!q->next)
 +              goto err_case;
 +      q->dep = kmalloc((1+q->depth*2)*sizeof(struct esfq_head), GFP_KERNEL);
 +      if (!q->dep)
 +              goto err_case;
 +      q->next = kmalloc(q->depth*sizeof(esfq_index), GFP_KERNEL);
 +      if (!q->next)
 +              goto err_case;
-+
 +      q->allot = kmalloc(q->depth*sizeof(short), GFP_KERNEL);
 +      if (!q->allot)
 +              goto err_case;
 +      q->allot = kmalloc(q->depth*sizeof(short), GFP_KERNEL);
 +      if (!q->allot)
 +              goto err_case;
@@ -696,7 +656,7 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +      q->qs = kmalloc(q->depth*sizeof(struct sk_buff_head), GFP_KERNEL);
 +      if (!q->qs)
 +              goto err_case;
 +      q->qs = kmalloc(q->depth*sizeof(struct sk_buff_head), GFP_KERNEL);
 +      if (!q->qs)
 +              goto err_case;
-+
++      
 +      for (i=0; i< q->hash_divisor; i++)
 +              q->ht[i] = q->depth;
 +      for (i=0; i<q->depth; i++) {
 +      for (i=0; i< q->hash_divisor; i++)
 +              q->ht[i] = q->depth;
 +      for (i=0; i<q->depth; i++) {
@@ -704,49 +664,87 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +              q->dep[i+q->depth].next = i+q->depth;
 +              q->dep[i+q->depth].prev = i+q->depth;
 +      }
 +              q->dep[i+q->depth].next = i+q->depth;
 +              q->dep[i+q->depth].prev = i+q->depth;
 +      }
-+
++      
 +      for (i=0; i<q->depth; i++)
 +              esfq_link(q, i);
 +      return 0;
 +err_case:
 +      for (i=0; i<q->depth; i++)
 +              esfq_link(q, i);
 +      return 0;
 +err_case:
-+      del_timer(&q->perturb_timer);
-+      if (q->ht)
-+              kfree(q->ht);
-+      if (q->dep)
-+              kfree(q->dep);
-+      if (q->next)
-+              kfree(q->next);
-+      if (q->allot)
-+              kfree(q->allot);
-+      if (q->hash)
-+              kfree(q->hash);
-+      if (q->qs)
-+              kfree(q->qs);
++      esfq_q_destroy(q);
 +      return -ENOBUFS;
 +}
 +
 +      return -ENOBUFS;
 +}
 +
-+static void esfq_destroy(struct Qdisc *sch)
++static int esfq_init(struct Qdisc *sch, struct rtattr *opt)
 +{
 +      struct esfq_sched_data *q = qdisc_priv(sch);
 +{
 +      struct esfq_sched_data *q = qdisc_priv(sch);
-+      del_timer(&q->perturb_timer);
-+      if(q->ht)
-+              kfree(q->ht);
-+      if(q->dep)
-+              kfree(q->dep);
-+      if(q->next)
-+              kfree(q->next);
-+      if(q->allot)
-+              kfree(q->allot);
-+      if(q->hash)
-+              kfree(q->hash);
-+      if(q->qs)
-+              kfree(q->qs);
++      int err;
++      
++      q->quantum = psched_mtu(sch->dev); /* default */
++      if ((err = esfq_q_init(q, opt)))
++              return err;
++
++      init_timer(&q->perturb_timer);
++      q->perturb_timer.data = (unsigned long)sch;
++      q->perturb_timer.function = esfq_perturbation;
++      if (q->perturb_period) {
++              q->perturb_timer.expires = jiffies + q->perturb_period;
++              add_timer(&q->perturb_timer);
++      }
++      
++      return 0;
++}
++
++static int esfq_change(struct Qdisc *sch, struct rtattr *opt)
++{
++      struct esfq_sched_data *q = qdisc_priv(sch);
++      struct esfq_sched_data new;
++      struct sk_buff *skb;
++      int err;
++      
++      /* set up new queue */
++      memset(&new, 0, sizeof(struct esfq_sched_data));
++      new.quantum = psched_mtu(sch->dev); /* default */
++      if ((err = esfq_q_init(&new, opt)))
++              return err;
++
++      /* copy all packets from the old queue to the new queue */
++      sch_tree_lock(sch);
++      while ((skb = esfq_q_dequeue(q)) != NULL)
++              esfq_q_enqueue(skb, &new, ESFQ_TAIL);
++      
++      /* clean up the old queue */
++      esfq_q_destroy(q);
++
++      /* copy elements of the new queue into the old queue */
++      q->perturb_period = new.perturb_period;
++      q->quantum        = new.quantum;
++      q->limit          = new.limit;
++      q->depth          = new.depth;
++      q->hash_divisor   = new.hash_divisor;
++      q->hash_kind      = new.hash_kind;
++      q->tail           = new.tail;
++      q->max_depth      = new.max_depth;
++      q->ht    = new.ht;
++      q->dep   = new.dep;
++      q->next  = new.next;
++      q->allot = new.allot;
++      q->hash  = new.hash;
++      q->qs    = new.qs;
++
++      /* finish up */
++      if (q->perturb_period) {
++              q->perturb_timer.expires = jiffies + q->perturb_period;
++              add_timer(&q->perturb_timer);
++      } else {
++              q->perturbation = 0;
++      }
++      sch_tree_unlock(sch);
++      return 0;
 +}
 +
 +static int esfq_dump(struct Qdisc *sch, struct sk_buff *skb)
 +{
 +      struct esfq_sched_data *q = qdisc_priv(sch);
 +}
 +
 +static int esfq_dump(struct Qdisc *sch, struct sk_buff *skb)
 +{
 +      struct esfq_sched_data *q = qdisc_priv(sch);
-+      unsigned char    *b = skb->tail;
++      unsigned char *b = skb->tail;
 +      struct tc_esfq_qopt opt;
 +
 +      opt.quantum = q->quantum;
 +      struct tc_esfq_qopt opt;
 +
 +      opt.quantum = q->quantum;
@@ -779,7 +777,7 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +      .init           =       esfq_init,
 +      .reset          =       esfq_reset,
 +      .destroy        =       esfq_destroy,
 +      .init           =       esfq_init,
 +      .reset          =       esfq_reset,
 +      .destroy        =       esfq_destroy,
-+      .change         =       NULL, /* esfq_change - needs more work */
++      .change         =       esfq_change,
 +      .dump           =       esfq_dump,
 +      .owner          =       THIS_MODULE,
 +};
 +      .dump           =       esfq_dump,
 +      .owner          =       THIS_MODULE,
 +};
@@ -788,7 +786,7 @@ Index: linux-2.6.23-rc6/net/sched/sch_esfq.c
 +{
 +      return register_qdisc(&esfq_qdisc_ops);
 +}
 +{
 +      return register_qdisc(&esfq_qdisc_ops);
 +}
-+static void __exit esfq_module_exit(void)
++static void __exit esfq_module_exit(void) 
 +{
 +      unregister_qdisc(&esfq_qdisc_ops);
 +}
 +{
 +      unregister_qdisc(&esfq_qdisc_ops);
 +}
This page took 0.043047 seconds and 4 git commands to generate.