source: trunk/target/linux/generic-2.6/patches-2.6.23/200-sched_esfq.patch @ 10174

Last change on this file since 10174 was 10174, checked in by florian, 8 years ago

Fix esfq compilation (#3005)

File size: 21.4 KB
  • include/linux/pkt_sched.h

    diff -Naur linux-2.6.21.5.orig/include/linux/pkt_sched.h linux-2.6.21.5/include/linux/pkt_sched.h
    old new  
    146146 * 
    147147 *      The only reason for this is efficiency, it is possible 
    148148 *      to change these parameters in compile time. 
     149 *       
     150 *      If you need to play with these values, use esfq instead. 
    149151 */ 
    150152 
     153/* ESFQ section */ 
     154 
     155enum 
     156{ 
     157        /* traditional */ 
     158        TCA_SFQ_HASH_CLASSIC, 
     159        TCA_SFQ_HASH_DST, 
     160        TCA_SFQ_HASH_SRC, 
     161        TCA_SFQ_HASH_FWMARK, 
     162        /* conntrack */ 
     163        TCA_SFQ_HASH_CTORIGDST, 
     164        TCA_SFQ_HASH_CTORIGSRC, 
     165        TCA_SFQ_HASH_CTREPLDST, 
     166        TCA_SFQ_HASH_CTREPLSRC, 
     167        TCA_SFQ_HASH_CTNATCHG, 
     168}; 
     169 
     170struct tc_esfq_qopt 
     171{ 
     172        unsigned        quantum;        /* Bytes per round allocated to flow */ 
     173        int             perturb_period; /* Period of hash perturbation */ 
     174        __u32           limit;          /* Maximal packets in queue */ 
     175        unsigned        divisor;        /* Hash divisor  */ 
     176        unsigned        flows;          /* Maximal number of flows  */ 
     177        unsigned        hash_kind;      /* Hash function to use for flow identification */ 
     178}; 
     179 
    151180/* RED section */ 
    152181 
    153182enum 
  • net/sched/Kconfig

    diff -Naur linux-2.6.21.5.orig/net/sched/Kconfig linux-2.6.21.5/net/sched/Kconfig
    old new  
    189189          To compile this code as a module, choose M here: the 
    190190          module will be called sch_sfq. 
    191191 
     192config NET_SCH_ESFQ 
     193        tristate "Enhanced Stochastic Fairness Queueing (ESFQ)" 
     194        ---help--- 
     195          Say Y here if you want to use the Enhanced Stochastic Fairness 
     196          Queueing (ESFQ) packet scheduling algorithm for some of your network 
     197          devices or as a leaf discipline for a classful qdisc such as HTB or 
     198          CBQ (see the top of <file:net/sched/sch_esfq.c> for details and 
     199          references to the SFQ algorithm). 
     200 
     201          This is an enchanced SFQ version which allows you to control some 
     202          hardcoded values in the SFQ scheduler. 
     203 
     204          ESFQ also adds control of the hash function used to identify packet 
     205          flows. The original SFQ discipline hashes by connection; ESFQ add 
     206          several other hashing methods, such as by src IP or by dst IP, which 
     207          can be more fair to users in some networking situations. 
     208           
     209          To compile this code as a module, choose M here: the 
     210          module will be called sch_esfq. 
     211 
     212config NET_SCH_ESFQ_NFCT 
     213        bool "Connection Tracking Hash Types" 
     214        depends on NET_SCH_ESFQ && NF_CONNTRACK 
     215        ---help--- 
     216          Say Y here to enable support for hashing based on netfilter connection 
     217          tracking information. This is useful for a router that is also using 
     218          NAT to connect privately-addressed hosts to the Internet. If you want 
     219          to provide fair distribution of upstream bandwidth, ESFQ must use 
     220          connection tracking information, since all outgoing packets will share 
     221          the same source address. 
     222 
    192223config NET_SCH_TEQL 
    193224        tristate "True Link Equalizer (TEQL)" 
    194225        ---help--- 
  • net/sched/Makefile

    diff -Naur linux-2.6.21.5.orig/net/sched/Makefile linux-2.6.21.5/net/sched/Makefile
    old new  
    2323obj-$(CONFIG_NET_SCH_INGRESS)   += sch_ingress.o  
    2424obj-$(CONFIG_NET_SCH_DSMARK)    += sch_dsmark.o 
    2525obj-$(CONFIG_NET_SCH_SFQ)       += sch_sfq.o 
     26obj-$(CONFIG_NET_SCH_ESFQ)      += sch_esfq.o 
    2627obj-$(CONFIG_NET_SCH_TBF)       += sch_tbf.o 
    2728obj-$(CONFIG_NET_SCH_TEQL)      += sch_teql.o 
    2829obj-$(CONFIG_NET_SCH_PRIO)      += sch_prio.o 
  • net/sched/sch_esfq.c

    diff -Naur linux-2.6.21.5.orig/net/sched/sch_esfq.c linux-2.6.21.5/net/sched/sch_esfq.c
    old new  
     1/* 
     2 * net/sched/sch_esfq.c Extended Stochastic Fairness Queueing discipline. 
     3 * 
     4 *              This program is free software; you can redistribute it and/or 
     5 *              modify it under the terms of the GNU General Public License 
     6 *              as published by the Free Software Foundation; either version 
     7 *              2 of the License, or (at your option) any later version. 
     8 * 
     9 * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 
     10 * 
     11 * Changes:     Alexander Atanasov, <alex@ssi.bg> 
     12 *              Added dynamic depth,limit,divisor,hash_kind options. 
     13 *              Added dst and src hashes. 
     14 * 
     15 *              Alexander Clouter, <alex@digriz.org.uk> 
     16 *              Ported ESFQ to Linux 2.6. 
     17 * 
     18 *              Corey Hickey, <bugfood-c@fatooh.org> 
     19 *              Maintenance of the Linux 2.6 port. 
     20 *              Added fwmark hash (thanks to Robert Kurjata). 
     21 *              Added usage of jhash. 
     22 *              Added conntrack support. 
     23 *              Added ctnatchg hash (thanks to Ben Pfountz). 
     24 */ 
     25 
     26#include <linux/module.h> 
     27#include <asm/uaccess.h> 
     28#include <asm/system.h> 
     29#include <linux/bitops.h> 
     30#include <linux/types.h> 
     31#include <linux/kernel.h> 
     32#include <linux/jiffies.h> 
     33#include <linux/string.h> 
     34#include <linux/mm.h> 
     35#include <linux/socket.h> 
     36#include <linux/sockios.h> 
     37#include <linux/in.h> 
     38#include <linux/errno.h> 
     39#include <linux/interrupt.h> 
     40#include <linux/if_ether.h> 
     41#include <linux/inet.h> 
     42#include <linux/netdevice.h> 
     43#include <linux/etherdevice.h> 
     44#include <linux/notifier.h> 
     45#include <linux/init.h> 
     46#include <net/ip.h> 
     47#include <linux/ipv6.h> 
     48#include <net/route.h> 
     49#include <linux/skbuff.h> 
     50#include <net/sock.h> 
     51#include <net/pkt_sched.h> 
     52#include <linux/jhash.h> 
     53#include <net/netfilter/nf_conntrack.h> 
     54 
     55/*      Stochastic Fairness Queuing algorithm. 
     56        For more comments look at sch_sfq.c. 
     57        The difference is that you can change limit, depth, 
     58        hash table size and choose alternate hash types. 
     59         
     60        classic:        same as in sch_sfq.c 
     61        dst:            destination IP address 
     62        src:            source IP address 
     63        fwmark:         netfilter mark value 
     64        ctorigdst:      original destination IP address 
     65        ctorigsrc:      original source IP address 
     66        ctrepldst:      reply destination IP address 
     67        ctreplsrc:      reply source IP  
     68         
     69*/ 
     70 
     71#define ESFQ_HEAD 0 
     72#define ESFQ_TAIL 1 
     73 
     74/* This type should contain at least SFQ_DEPTH*2 values */ 
     75typedef unsigned int esfq_index; 
     76 
     77struct esfq_head 
     78{ 
     79        esfq_index      next; 
     80        esfq_index      prev; 
     81}; 
     82 
     83struct esfq_sched_data 
     84{ 
     85/* Parameters */ 
     86        int             perturb_period; 
     87        unsigned        quantum;        /* Allotment per round: MUST BE >= MTU */ 
     88        int             limit; 
     89        unsigned        depth; 
     90        unsigned        hash_divisor; 
     91        unsigned        hash_kind; 
     92/* Variables */ 
     93        struct timer_list perturb_timer; 
     94        int             perturbation; 
     95        esfq_index      tail;           /* Index of current slot in round */ 
     96        esfq_index      max_depth;      /* Maximal depth */ 
     97 
     98        esfq_index      *ht;                    /* Hash table */ 
     99        esfq_index      *next;                  /* Active slots link */ 
     100        short           *allot;                 /* Current allotment per slot */ 
     101        unsigned short  *hash;                  /* Hash value indexed by slots */ 
     102        struct sk_buff_head     *qs;            /* Slot queue */ 
     103        struct esfq_head        *dep;           /* Linked list of slots, indexed by depth */ 
     104}; 
     105 
     106/* This contains the info we will hash. */ 
     107struct esfq_packet_info 
     108{ 
     109        u32     proto;          /* protocol or port */ 
     110        u32     src;            /* source from packet header */ 
     111        u32     dst;            /* destination from packet header */ 
     112        u32     ctorigsrc;      /* original source from conntrack */ 
     113        u32     ctorigdst;      /* original destination from conntrack */ 
     114        u32     ctreplsrc;      /* reply source from conntrack */ 
     115        u32     ctrepldst;      /* reply destination from conntrack */ 
     116        u32     mark;           /* netfilter mark (fwmark) */ 
     117}; 
     118 
     119static __inline__ unsigned esfq_jhash_1word(struct esfq_sched_data *q,u32 a) 
     120{ 
     121        return jhash_1word(a, q->perturbation) & (q->hash_divisor-1); 
     122} 
     123 
     124static __inline__ unsigned esfq_jhash_2words(struct esfq_sched_data *q, u32 a, u32 b) 
     125{ 
     126        return jhash_2words(a, b, q->perturbation) & (q->hash_divisor-1); 
     127} 
     128 
     129static __inline__ unsigned esfq_jhash_3words(struct esfq_sched_data *q, u32 a, u32 b, u32 c) 
     130{ 
     131        return jhash_3words(a, b, c, q->perturbation) & (q->hash_divisor-1); 
     132} 
     133 
     134static unsigned esfq_hash(struct esfq_sched_data *q, struct sk_buff *skb) 
     135{ 
     136        struct esfq_packet_info info; 
     137#ifdef CONFIG_NET_SCH_ESFQ_NFCT 
     138        enum ip_conntrack_info ctinfo; 
     139        struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 
     140#endif 
     141         
     142        switch (skb->protocol) { 
     143        case __constant_htons(ETH_P_IP): 
     144        { 
     145                struct iphdr *iph = ip_hdr(skb); 
     146                info.dst = iph->daddr; 
     147                info.src = iph->saddr; 
     148                if (!(iph->frag_off&htons(IP_MF|IP_OFFSET)) && 
     149                    (iph->protocol == IPPROTO_TCP || 
     150                     iph->protocol == IPPROTO_UDP || 
     151                     iph->protocol == IPPROTO_SCTP || 
     152                     iph->protocol == IPPROTO_DCCP || 
     153                     iph->protocol == IPPROTO_ESP)) 
     154                        info.proto = *(((u32*)iph) + iph->ihl); 
     155                else 
     156                        info.proto = iph->protocol; 
     157                break; 
     158        } 
     159        case __constant_htons(ETH_P_IPV6): 
     160        { 
     161                struct ipv6hdr *iph = ipv6_hdr(skb); 
     162                /* Hash ipv6 addresses into a u32. This isn't ideal, 
     163                 * but the code is simple. */ 
     164                info.dst = jhash2(iph->daddr.s6_addr32, 4, q->perturbation); 
     165                info.src = jhash2(iph->saddr.s6_addr32, 4, q->perturbation); 
     166                if (iph->nexthdr == IPPROTO_TCP || 
     167                    iph->nexthdr == IPPROTO_UDP || 
     168                    iph->nexthdr == IPPROTO_SCTP || 
     169                    iph->nexthdr == IPPROTO_DCCP || 
     170                    iph->nexthdr == IPPROTO_ESP) 
     171                        info.proto = *(u32*)&iph[1]; 
     172                else 
     173                        info.proto = iph->nexthdr; 
     174                break; 
     175        } 
     176        default: 
     177                info.dst   = (u32)(unsigned long)skb->dst; 
     178                info.src   = (u32)(unsigned long)skb->sk; 
     179                info.proto = skb->protocol; 
     180        } 
     181 
     182        info.mark = skb->mark; 
     183 
     184#ifdef CONFIG_NET_SCH_ESFQ_NFCT 
     185        /* defaults if there is no conntrack info */ 
     186        info.ctorigsrc = info.src; 
     187        info.ctorigdst = info.dst; 
     188        info.ctreplsrc = info.dst; 
     189        info.ctrepldst = info.src; 
     190        /* collect conntrack info */ 
     191        if (ct && ct != &nf_conntrack_untracked) { 
     192                if (skb->protocol == __constant_htons(ETH_P_IP)) { 
     193                        info.ctorigsrc = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; 
     194                        info.ctorigdst = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip; 
     195                        info.ctreplsrc = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip; 
     196                        info.ctrepldst = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip; 
     197                } 
     198                else if (skb->protocol == __constant_htons(ETH_P_IPV6)) { 
     199                        /* Again, hash ipv6 addresses into a single u32. */ 
     200                        info.ctorigsrc = jhash2(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip6, 4, q->perturbation); 
     201                        info.ctorigdst = jhash2(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip6, 4, q->perturbation); 
     202                        info.ctreplsrc = jhash2(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip6, 4, q->perturbation); 
     203                        info.ctrepldst = jhash2(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip6, 4, q->perturbation); 
     204                } 
     205 
     206        } 
     207#endif 
     208 
     209        switch(q->hash_kind) { 
     210        case TCA_SFQ_HASH_CLASSIC: 
     211                return esfq_jhash_3words(q, info.dst, info.src, info.proto); 
     212        case TCA_SFQ_HASH_DST: 
     213                return esfq_jhash_1word(q, info.dst); 
     214        case TCA_SFQ_HASH_SRC: 
     215                return esfq_jhash_1word(q, info.src); 
     216        case TCA_SFQ_HASH_FWMARK: 
     217                return esfq_jhash_1word(q, info.mark); 
     218#ifdef CONFIG_NET_SCH_ESFQ_NFCT 
     219        case TCA_SFQ_HASH_CTORIGDST: 
     220                return esfq_jhash_1word(q, info.ctorigdst); 
     221        case TCA_SFQ_HASH_CTORIGSRC: 
     222                return esfq_jhash_1word(q, info.ctorigsrc); 
     223        case TCA_SFQ_HASH_CTREPLDST: 
     224                return esfq_jhash_1word(q, info.ctrepldst); 
     225        case TCA_SFQ_HASH_CTREPLSRC: 
     226                return esfq_jhash_1word(q, info.ctreplsrc); 
     227        case TCA_SFQ_HASH_CTNATCHG: 
     228        { 
     229                if (info.ctorigdst == info.ctreplsrc) 
     230                        return esfq_jhash_1word(q, info.ctorigsrc); 
     231                return esfq_jhash_1word(q, info.ctreplsrc); 
     232        } 
     233#endif 
     234        default: 
     235                if (net_ratelimit()) 
     236                        printk(KERN_WARNING "ESFQ: Unknown hash method. Falling back to classic.\n"); 
     237        } 
     238        return esfq_jhash_3words(q, info.dst, info.src, info.proto); 
     239} 
     240 
     241static inline void esfq_link(struct esfq_sched_data *q, esfq_index x) 
     242{ 
     243        esfq_index p, n; 
     244        int d = q->qs[x].qlen + q->depth; 
     245 
     246        p = d; 
     247        n = q->dep[d].next; 
     248        q->dep[x].next = n; 
     249        q->dep[x].prev = p; 
     250        q->dep[p].next = q->dep[n].prev = x; 
     251} 
     252 
     253static inline void esfq_dec(struct esfq_sched_data *q, esfq_index x) 
     254{ 
     255        esfq_index p, n; 
     256 
     257        n = q->dep[x].next; 
     258        p = q->dep[x].prev; 
     259        q->dep[p].next = n; 
     260        q->dep[n].prev = p; 
     261 
     262        if (n == p && q->max_depth == q->qs[x].qlen + 1) 
     263                q->max_depth--; 
     264 
     265        esfq_link(q, x); 
     266} 
     267 
     268static inline void esfq_inc(struct esfq_sched_data *q, esfq_index x) 
     269{ 
     270        esfq_index p, n; 
     271        int d; 
     272 
     273        n = q->dep[x].next; 
     274        p = q->dep[x].prev; 
     275        q->dep[p].next = n; 
     276        q->dep[n].prev = p; 
     277        d = q->qs[x].qlen; 
     278        if (q->max_depth < d) 
     279                q->max_depth = d; 
     280 
     281        esfq_link(q, x); 
     282} 
     283 
     284static unsigned int esfq_drop(struct Qdisc *sch) 
     285{ 
     286        struct esfq_sched_data *q = qdisc_priv(sch); 
     287        esfq_index d = q->max_depth; 
     288        struct sk_buff *skb; 
     289        unsigned int len; 
     290 
     291        /* Queue is full! Find the longest slot and 
     292           drop a packet from it */ 
     293 
     294        if (d > 1) { 
     295                esfq_index x = q->dep[d+q->depth].next; 
     296                skb = q->qs[x].prev; 
     297                len = skb->len; 
     298                __skb_unlink(skb, &q->qs[x]); 
     299                kfree_skb(skb); 
     300                esfq_dec(q, x); 
     301                sch->q.qlen--; 
     302                sch->qstats.drops++; 
     303                sch->qstats.backlog -= len; 
     304                return len; 
     305        } 
     306 
     307        if (d == 1) { 
     308                /* It is difficult to believe, but ALL THE SLOTS HAVE LENGTH 1. */ 
     309                d = q->next[q->tail]; 
     310                q->next[q->tail] = q->next[d]; 
     311                q->allot[q->next[d]] += q->quantum; 
     312                skb = q->qs[d].prev; 
     313                len = skb->len; 
     314                __skb_unlink(skb, &q->qs[d]); 
     315                kfree_skb(skb); 
     316                esfq_dec(q, d); 
     317                sch->q.qlen--; 
     318                q->ht[q->hash[d]] = q->depth; 
     319                sch->qstats.drops++; 
     320                sch->qstats.backlog -= len; 
     321                return len; 
     322        } 
     323 
     324        return 0; 
     325} 
     326 
     327static void esfq_q_enqueue(struct sk_buff *skb, struct esfq_sched_data *q, unsigned int end) 
     328{ 
     329        unsigned hash = esfq_hash(q, skb); 
     330        unsigned depth = q->depth; 
     331        esfq_index x; 
     332 
     333        x = q->ht[hash]; 
     334        if (x == depth) { 
     335                q->ht[hash] = x = q->dep[depth].next; 
     336                q->hash[x] = hash; 
     337        } 
     338 
     339        if (end == ESFQ_TAIL) 
     340                __skb_queue_tail(&q->qs[x], skb); 
     341        else 
     342                __skb_queue_head(&q->qs[x], skb); 
     343 
     344        esfq_inc(q, x); 
     345        if (q->qs[x].qlen == 1) {               /* The flow is new */ 
     346                if (q->tail == depth) { /* It is the first flow */ 
     347                        q->tail = x; 
     348                        q->next[x] = x; 
     349                        q->allot[x] = q->quantum; 
     350                } else { 
     351                        q->next[x] = q->next[q->tail]; 
     352                        q->next[q->tail] = x; 
     353                        q->tail = x; 
     354                } 
     355        } 
     356} 
     357 
     358static int esfq_enqueue(struct sk_buff *skb, struct Qdisc* sch) 
     359{ 
     360        struct esfq_sched_data *q = qdisc_priv(sch); 
     361        esfq_q_enqueue(skb, q, ESFQ_TAIL); 
     362        sch->qstats.backlog += skb->len; 
     363        if (++sch->q.qlen < q->limit-1) { 
     364                sch->bstats.bytes += skb->len; 
     365                sch->bstats.packets++; 
     366                return 0; 
     367        } 
     368 
     369        sch->qstats.drops++; 
     370        esfq_drop(sch); 
     371        return NET_XMIT_CN; 
     372} 
     373 
     374 
     375static int esfq_requeue(struct sk_buff *skb, struct Qdisc* sch) 
     376{ 
     377        struct esfq_sched_data *q = qdisc_priv(sch); 
     378        esfq_q_enqueue(skb, q, ESFQ_HEAD); 
     379        sch->qstats.backlog += skb->len; 
     380        if (++sch->q.qlen < q->limit - 1) { 
     381                sch->qstats.requeues++; 
     382                return 0; 
     383        } 
     384 
     385        sch->qstats.drops++; 
     386        esfq_drop(sch); 
     387        return NET_XMIT_CN; 
     388} 
     389 
     390static struct sk_buff *esfq_q_dequeue(struct esfq_sched_data *q) 
     391{ 
     392        struct sk_buff *skb; 
     393        unsigned depth = q->depth; 
     394        esfq_index a, old_a; 
     395 
     396        /* No active slots */ 
     397        if (q->tail == depth) 
     398                return NULL; 
     399         
     400        a = old_a = q->next[q->tail]; 
     401         
     402        /* Grab packet */ 
     403        skb = __skb_dequeue(&q->qs[a]); 
     404        esfq_dec(q, a); 
     405         
     406        /* Is the slot empty? */ 
     407        if (q->qs[a].qlen == 0) { 
     408                q->ht[q->hash[a]] = depth; 
     409                a = q->next[a]; 
     410                if (a == old_a) { 
     411                        q->tail = depth; 
     412                        return skb; 
     413                } 
     414                q->next[q->tail] = a; 
     415                q->allot[a] += q->quantum; 
     416        } else if ((q->allot[a] -= skb->len) <= 0) { 
     417                q->tail = a; 
     418                a = q->next[a]; 
     419                q->allot[a] += q->quantum; 
     420        } 
     421         
     422        return skb; 
     423} 
     424 
     425static struct sk_buff *esfq_dequeue(struct Qdisc* sch) 
     426{ 
     427        struct esfq_sched_data *q = qdisc_priv(sch); 
     428        struct sk_buff *skb; 
     429 
     430        skb = esfq_q_dequeue(q); 
     431        if (skb == NULL) 
     432                return NULL; 
     433        sch->q.qlen--; 
     434        sch->qstats.backlog -= skb->len; 
     435        return skb; 
     436} 
     437 
     438static void esfq_q_destroy(struct esfq_sched_data *q) 
     439{ 
     440        del_timer(&q->perturb_timer); 
     441        if(q->ht) 
     442                kfree(q->ht); 
     443        if(q->dep) 
     444                kfree(q->dep); 
     445        if(q->next) 
     446                kfree(q->next); 
     447        if(q->allot) 
     448                kfree(q->allot); 
     449        if(q->hash) 
     450                kfree(q->hash); 
     451        if(q->qs) 
     452                kfree(q->qs); 
     453} 
     454 
     455static void esfq_destroy(struct Qdisc *sch) 
     456{ 
     457        struct esfq_sched_data *q = qdisc_priv(sch); 
     458        esfq_q_destroy(q); 
     459} 
     460 
     461 
     462static void esfq_reset(struct Qdisc* sch) 
     463{ 
     464        struct sk_buff *skb; 
     465 
     466        while ((skb = esfq_dequeue(sch)) != NULL) 
     467                kfree_skb(skb); 
     468} 
     469 
     470static void esfq_perturbation(unsigned long arg) 
     471{ 
     472        struct Qdisc *sch = (struct Qdisc*)arg; 
     473        struct esfq_sched_data *q = qdisc_priv(sch); 
     474 
     475        q->perturbation = net_random()&0x1F; 
     476 
     477        if (q->perturb_period) { 
     478                q->perturb_timer.expires = jiffies + q->perturb_period; 
     479                add_timer(&q->perturb_timer); 
     480        } 
     481} 
     482 
     483static unsigned int esfq_check_hash(unsigned int kind) 
     484{ 
     485        switch (kind) { 
     486        case TCA_SFQ_HASH_CTORIGDST: 
     487        case TCA_SFQ_HASH_CTORIGSRC: 
     488        case TCA_SFQ_HASH_CTREPLDST: 
     489        case TCA_SFQ_HASH_CTREPLSRC: 
     490        case TCA_SFQ_HASH_CTNATCHG: 
     491#ifndef CONFIG_NET_SCH_ESFQ_NFCT 
     492        { 
     493                if (net_ratelimit()) 
     494                        printk(KERN_WARNING "ESFQ: Conntrack hash types disabled in kernel config. Falling back to classic.\n"); 
     495                return TCA_SFQ_HASH_CLASSIC; 
     496        } 
     497#endif 
     498        case TCA_SFQ_HASH_CLASSIC: 
     499        case TCA_SFQ_HASH_DST: 
     500        case TCA_SFQ_HASH_SRC: 
     501        case TCA_SFQ_HASH_FWMARK: 
     502                return kind; 
     503        default: 
     504        { 
     505                if (net_ratelimit()) 
     506                        printk(KERN_WARNING "ESFQ: Unknown hash type. Falling back to classic.\n"); 
     507                return TCA_SFQ_HASH_CLASSIC; 
     508        } 
     509        } 
     510} 
     511         
     512static int esfq_q_init(struct esfq_sched_data *q, struct rtattr *opt) 
     513{ 
     514        struct tc_esfq_qopt *ctl = RTA_DATA(opt); 
     515        esfq_index p = ~0U/2; 
     516        int i; 
     517         
     518        if (opt && opt->rta_len < RTA_LENGTH(sizeof(*ctl))) 
     519                return -EINVAL; 
     520 
     521        q->perturbation = 0; 
     522        q->hash_kind = TCA_SFQ_HASH_CLASSIC; 
     523        q->max_depth = 0; 
     524        if (opt == NULL) { 
     525                q->perturb_period = 0; 
     526                q->hash_divisor = 1024; 
     527                q->tail = q->limit = q->depth = 128; 
     528                 
     529        } else { 
     530                struct tc_esfq_qopt *ctl = RTA_DATA(opt); 
     531                if (ctl->quantum) 
     532                        q->quantum = ctl->quantum; 
     533                q->perturb_period = ctl->perturb_period*HZ; 
     534                q->hash_divisor = ctl->divisor ? : 1024; 
     535                q->tail = q->limit = q->depth = ctl->flows ? : 128; 
     536                 
     537                if ( q->depth > p - 1 ) 
     538                        return -EINVAL; 
     539                 
     540                if (ctl->limit) 
     541                        q->limit = min_t(u32, ctl->limit, q->depth); 
     542                 
     543                if (ctl->hash_kind) { 
     544                        q->hash_kind = esfq_check_hash(ctl->hash_kind); 
     545                } 
     546        } 
     547         
     548        q->ht = kmalloc(q->hash_divisor*sizeof(esfq_index), GFP_KERNEL); 
     549        if (!q->ht) 
     550                goto err_case; 
     551        q->dep = kmalloc((1+q->depth*2)*sizeof(struct esfq_head), GFP_KERNEL); 
     552        if (!q->dep) 
     553                goto err_case; 
     554        q->next = kmalloc(q->depth*sizeof(esfq_index), GFP_KERNEL); 
     555        if (!q->next) 
     556                goto err_case; 
     557        q->allot = kmalloc(q->depth*sizeof(short), GFP_KERNEL); 
     558        if (!q->allot) 
     559                goto err_case; 
     560        q->hash = kmalloc(q->depth*sizeof(unsigned short), GFP_KERNEL); 
     561        if (!q->hash) 
     562                goto err_case; 
     563        q->qs = kmalloc(q->depth*sizeof(struct sk_buff_head), GFP_KERNEL); 
     564        if (!q->qs) 
     565                goto err_case; 
     566         
     567        for (i=0; i< q->hash_divisor; i++) 
     568                q->ht[i] = q->depth; 
     569        for (i=0; i<q->depth; i++) { 
     570                skb_queue_head_init(&q->qs[i]); 
     571                q->dep[i+q->depth].next = i+q->depth; 
     572                q->dep[i+q->depth].prev = i+q->depth; 
     573        } 
     574         
     575        for (i=0; i<q->depth; i++) 
     576                esfq_link(q, i); 
     577        return 0; 
     578err_case: 
     579        esfq_q_destroy(q); 
     580        return -ENOBUFS; 
     581} 
     582 
     583static int esfq_init(struct Qdisc *sch, struct rtattr *opt) 
     584{ 
     585        struct esfq_sched_data *q = qdisc_priv(sch); 
     586        int err; 
     587         
     588        q->quantum = psched_mtu(sch->dev); /* default */ 
     589        if ((err = esfq_q_init(q, opt))) 
     590                return err; 
     591 
     592        init_timer(&q->perturb_timer); 
     593        q->perturb_timer.data = (unsigned long)sch; 
     594        q->perturb_timer.function = esfq_perturbation; 
     595        if (q->perturb_period) { 
     596                q->perturb_timer.expires = jiffies + q->perturb_period; 
     597                add_timer(&q->perturb_timer); 
     598        } 
     599         
     600        return 0; 
     601} 
     602 
     603static int esfq_change(struct Qdisc *sch, struct rtattr *opt) 
     604{ 
     605        struct esfq_sched_data *q = qdisc_priv(sch); 
     606        struct esfq_sched_data new; 
     607        struct sk_buff *skb; 
     608        int err; 
     609         
     610        /* set up new queue */ 
     611        memset(&new, 0, sizeof(struct esfq_sched_data)); 
     612        new.quantum = psched_mtu(sch->dev); /* default */ 
     613        if ((err = esfq_q_init(&new, opt))) 
     614                return err; 
     615 
     616        /* copy all packets from the old queue to the new queue */ 
     617        sch_tree_lock(sch); 
     618        while ((skb = esfq_q_dequeue(q)) != NULL) 
     619                esfq_q_enqueue(skb, &new, ESFQ_TAIL); 
     620         
     621        /* clean up the old queue */ 
     622        esfq_q_destroy(q); 
     623 
     624        /* copy elements of the new queue into the old queue */ 
     625        q->perturb_period = new.perturb_period; 
     626        q->quantum        = new.quantum; 
     627        q->limit          = new.limit; 
     628        q->depth          = new.depth; 
     629        q->hash_divisor   = new.hash_divisor; 
     630        q->hash_kind      = new.hash_kind; 
     631        q->tail           = new.tail; 
     632        q->max_depth      = new.max_depth; 
     633        q->ht    = new.ht; 
     634        q->dep   = new.dep; 
     635        q->next  = new.next; 
     636        q->allot = new.allot; 
     637        q->hash  = new.hash; 
     638        q->qs    = new.qs; 
     639 
     640        /* finish up */ 
     641        if (q->perturb_period) { 
     642                q->perturb_timer.expires = jiffies + q->perturb_period; 
     643                add_timer(&q->perturb_timer); 
     644        } else { 
     645                q->perturbation = 0; 
     646        } 
     647        sch_tree_unlock(sch); 
     648        return 0; 
     649} 
     650 
     651static int esfq_dump(struct Qdisc *sch, struct sk_buff *skb) 
     652{ 
     653        struct esfq_sched_data *q = qdisc_priv(sch); 
     654        unsigned char *b = skb->tail; 
     655        struct tc_esfq_qopt opt; 
     656 
     657        opt.quantum = q->quantum; 
     658        opt.perturb_period = q->perturb_period/HZ; 
     659 
     660        opt.limit = q->limit; 
     661        opt.divisor = q->hash_divisor; 
     662        opt.flows = q->depth; 
     663        opt.hash_kind = q->hash_kind; 
     664 
     665        RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); 
     666 
     667        return skb->len; 
     668 
     669rtattr_failure: 
     670        skb_trim(skb, b - skb->data); 
     671        return -1; 
     672} 
     673 
     674static struct Qdisc_ops esfq_qdisc_ops = 
     675{ 
     676        .next           =       NULL, 
     677        .cl_ops         =       NULL, 
     678        .id             =       "esfq", 
     679        .priv_size      =       sizeof(struct esfq_sched_data), 
     680        .enqueue        =       esfq_enqueue, 
     681        .dequeue        =       esfq_dequeue, 
     682        .requeue        =       esfq_requeue, 
     683        .drop           =       esfq_drop, 
     684        .init           =       esfq_init, 
     685        .reset          =       esfq_reset, 
     686        .destroy        =       esfq_destroy, 
     687        .change         =       esfq_change, 
     688        .dump           =       esfq_dump, 
     689        .owner          =       THIS_MODULE, 
     690}; 
     691 
     692static int __init esfq_module_init(void) 
     693{ 
     694        return register_qdisc(&esfq_qdisc_ops); 
     695} 
     696static void __exit esfq_module_exit(void)  
     697{ 
     698        unregister_qdisc(&esfq_qdisc_ops); 
     699} 
     700module_init(esfq_module_init) 
     701module_exit(esfq_module_exit) 
     702MODULE_LICENSE("GPL"); 
Note: See TracBrowser for help on using the repository browser.