The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netpfil/ipfw/dn_aqm_codel.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * Codel - The Controlled-Delay Active Queue Management algorithm.
    3  *
    4  * $FreeBSD$
    5  * 
    6  * Copyright (C) 2016 Centre for Advanced Internet Architectures,
    7  *  Swinburne University of Technology, Melbourne, Australia.
    8  * Portions of this code were made possible in part by a gift from 
    9  *  The Comcast Innovation Fund.
   10  * Implemented by Rasool Al-Saadi <ralsaadi@swin.edu.au>
   11  *
   12  * Redistribution and use in source and binary forms, with or without
   13  * modification, are permitted provided that the following conditions
   14  * are met:
   15  * 1. Redistributions of source code must retain the above copyright
   16  *    notice, this list of conditions and the following disclaimer.
   17  * 2. Redistributions in binary form must reproduce the above copyright
   18  *    notice, this list of conditions and the following disclaimer in the
   19  *    documentation and/or other materials provided with the distribution.
   20  *
   21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   24  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   31  * SUCH DAMAGE.
   32  */
   33 
   34 #include <sys/cdefs.h>
   35 #include "opt_inet6.h"
   36 
   37 #include <sys/param.h>
   38 #include <sys/systm.h>
   39 #include <sys/malloc.h>
   40 #include <sys/mbuf.h>
   41 #include <sys/kernel.h>
   42 #include <sys/lock.h>
   43 #include <sys/module.h>
   44 #include <sys/priv.h>
   45 #include <sys/proc.h>
   46 #include <sys/rwlock.h>
   47 #include <sys/socket.h>
   48 #include <sys/time.h>
   49 #include <sys/sysctl.h>
   50 
   51 #include <net/if.h>     /* IFNAMSIZ, struct ifaddr, ifq head, lock.h mutex.h */
   52 #include <net/netisr.h>
   53 #include <net/vnet.h>
   54 
   55 #include <netinet/in.h>
   56 #include <netinet/ip.h>         /* ip_len, ip_off */
   57 #include <netinet/ip_var.h>     /* ip_output(), IP_FORWARDING */
   58 #include <netinet/ip_fw.h>
   59 #include <netinet/ip_dummynet.h>
   60 #include <netinet/if_ether.h> /* various ether_* routines */
   61 #include <netinet/ip6.h>       /* for ip6_input, ip6_output prototypes */
   62 #include <netinet6/ip6_var.h>
   63 #include <netpfil/ipfw/dn_heap.h>
   64 
   65 #ifdef NEW_AQM
   66 #include <netpfil/ipfw/ip_fw_private.h>
   67 #include <netpfil/ipfw/ip_dn_private.h>
   68 #include <netpfil/ipfw/dn_aqm.h>
   69 #include <netpfil/ipfw/dn_aqm_codel.h>
   70 #include <netpfil/ipfw/dn_sched.h>
   71 
   72 #define DN_AQM_CODEL 1
   73 
   74 static struct dn_aqm codel_desc;
   75 
   76 /* default codel parameters */
   77 struct dn_aqm_codel_parms codel_sysctl = {5000 * AQM_TIME_1US,
   78         100000 * AQM_TIME_1US, 0};
   79 
   80 static int
   81 codel_sysctl_interval_handler(SYSCTL_HANDLER_ARGS)
   82 {
   83         int error;
   84         long  value;
   85 
   86         value = codel_sysctl.interval;
   87         value /= AQM_TIME_1US;
   88         error = sysctl_handle_long(oidp, &value, 0, req);
   89         if (error != 0 || req->newptr == NULL)
   90                 return (error);
   91         if (value < 1 || value > 100 * AQM_TIME_1S)
   92                 return (EINVAL);
   93         codel_sysctl.interval = value * AQM_TIME_1US ;
   94         return (0);
   95 }
   96 
   97 static int
   98 codel_sysctl_target_handler(SYSCTL_HANDLER_ARGS)
   99 {
  100         int error;
  101         long  value;
  102 
  103         value = codel_sysctl.target;
  104         value /= AQM_TIME_1US;
  105         error = sysctl_handle_long(oidp, &value, 0, req);
  106         if (error != 0 || req->newptr == NULL)
  107                 return (error);
  108         D("%ld", value);
  109         if (value < 1 || value > 5 * AQM_TIME_1S)
  110                 return (EINVAL);
  111         codel_sysctl.target = value * AQM_TIME_1US ;
  112         return (0);
  113 }
  114 
  115 /* defining Codel sysctl variables */
  116 SYSBEGIN(f4)
  117 
  118 SYSCTL_DECL(_net_inet);
  119 SYSCTL_DECL(_net_inet_ip);
  120 SYSCTL_DECL(_net_inet_ip_dummynet);
  121 static SYSCTL_NODE(_net_inet_ip_dummynet, OID_AUTO, codel,
  122     CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
  123     "CODEL");
  124 
  125 #ifdef SYSCTL_NODE
  126 SYSCTL_PROC(_net_inet_ip_dummynet_codel, OID_AUTO, target,
  127     CTLTYPE_LONG | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
  128     NULL, 0,codel_sysctl_target_handler, "L",
  129     "CoDel target in microsecond");
  130 
  131 SYSCTL_PROC(_net_inet_ip_dummynet_codel, OID_AUTO, interval,
  132     CTLTYPE_LONG | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
  133     NULL, 0, codel_sysctl_interval_handler, "L",
  134     "CoDel interval in microsecond");
  135 #endif
  136 
  137 /* This function computes codel_interval/sqrt(count) 
  138  *  Newton's method of approximation is used to compute 1/sqrt(count).
  139  * http://betterexplained.com/articles/
  140  *      understanding-quakes-fast-inverse-square-root/ 
  141  */
  142 aqm_time_t
  143 control_law(struct codel_status *cst, struct dn_aqm_codel_parms *cprms,
  144         aqm_time_t t)
  145 {
  146         uint32_t count;
  147         uint64_t temp;
  148         count = cst->count;
  149 
  150         /* we don't calculate isqrt(1) to get more accurate result*/
  151         if (count == 1) {
  152                 /* prepare isqrt (old guess) for the next iteration i.e. 1/sqrt(2)*/
  153                 cst->isqrt = (1UL<< FIX_POINT_BITS) * 7/10;
  154                 /* return time + isqrt(1)*interval */
  155                 return t + cprms->interval;
  156         }
  157 
  158         /* newguess = g(1.5 - 0.5*c*g^2)
  159          * Multiplying both sides by 2 to make all the constants integers
  160          * newguess * 2  = g(3 - c*g^2) g=old guess, c=count
  161          * So, newguess = newguess /2
  162          * Fixed point operations are used here.
  163          */
  164 
  165         /* Calculate g^2 */
  166         temp = (uint32_t) cst->isqrt * cst->isqrt;
  167         /* Calculate (3 - c*g^2) i.e. (3 - c * temp) */
  168         temp = (3ULL<< (FIX_POINT_BITS*2)) - (count * temp);
  169 
  170         /*
  171          * Divide by 2 because we multiplied the original equation by two
  172          * Also, we shift the result by 8 bits to prevent overflow.
  173          * */
  174         temp >>= (1 + 8);
  175 
  176         /*  Now, temp = (1.5 - 0.5*c*g^2)
  177          * Calculate g (1.5 - 0.5*c*g^2) i.e. g * temp
  178          */
  179         temp = (cst->isqrt * temp) >> (FIX_POINT_BITS + FIX_POINT_BITS - 8);
  180         cst->isqrt = temp;
  181 
  182          /* calculate codel_interval/sqrt(count) */
  183          return t + ((cprms->interval * temp) >> FIX_POINT_BITS);
  184 }
  185 
  186 /*
  187  * Extract a packet from the head of queue 'q'
  188  * Return a packet or NULL if the queue is empty.
  189  * Also extract packet's timestamp from mtag.
  190  */
  191 struct mbuf *
  192 codel_extract_head(struct dn_queue *q, aqm_time_t *pkt_ts)
  193 {
  194         struct m_tag *mtag;
  195         struct mbuf *m;
  196 
  197 next:   m = q->mq.head;
  198         if (m == NULL)
  199                 return m;
  200         q->mq.head = m->m_nextpkt;
  201 
  202         /* Update stats */
  203         update_stats(q, -m->m_pkthdr.len, 0);
  204 
  205         if (q->ni.length == 0) /* queue is now idle */
  206                         q->q_time = V_dn_cfg.curr_time;
  207 
  208         /* extract packet TS*/
  209         mtag = m_tag_locate(m, MTAG_ABI_COMPAT, DN_AQM_MTAG_TS, NULL);
  210         if (mtag == NULL) {
  211                 D("Codel timestamp mtag not found!");
  212                 *pkt_ts = 0;
  213         } else {
  214                 *pkt_ts = *(aqm_time_t *)(mtag + 1);
  215                 m_tag_delete(m,mtag); 
  216         }
  217         if (m->m_pkthdr.rcvif != NULL &&
  218             __predict_false(m_rcvif_restore(m) == NULL)) {
  219                 m_freem(m);
  220                 goto next;
  221         }
  222 
  223         return m;
  224 }
  225 
  226 /*
  227  * Enqueue a packet 'm' in queue 'q'
  228  */
  229 static int
  230 aqm_codel_enqueue(struct dn_queue *q, struct mbuf *m)
  231 {
  232         struct dn_fs *f;
  233         uint64_t len;
  234         struct codel_status *cst;       /*codel status variables */
  235         struct m_tag *mtag;
  236 
  237         f = &(q->fs->fs);
  238         len = m->m_pkthdr.len;
  239         cst = q->aqm_status;
  240         if(!cst) {
  241                 D("Codel queue is not initialized\n");
  242                 goto drop;
  243         }
  244 
  245         /* Finding maximum packet size */
  246         // XXX we can get MTU from driver instead 
  247         if (len > cst->maxpkt_size)
  248                 cst->maxpkt_size = len;
  249 
  250         /* check for queue size and drop the tail if exceed queue limit*/
  251         if (f->flags & DN_QSIZE_BYTES) {
  252                 if ( q->ni.len_bytes > f->qsize)
  253                         goto drop;
  254         }
  255         else {
  256                 if ( q->ni.length >= f->qsize)
  257                         goto drop;
  258         }
  259 
  260         /* Add timestamp as mtag */
  261         mtag = m_tag_locate(m, MTAG_ABI_COMPAT, DN_AQM_MTAG_TS, NULL);
  262         if (mtag == NULL)
  263                 mtag = m_tag_alloc(MTAG_ABI_COMPAT, DN_AQM_MTAG_TS,
  264                         sizeof(aqm_time_t), M_NOWAIT);
  265         if (mtag == NULL)
  266                 goto drop;
  267 
  268         *(aqm_time_t *)(mtag + 1) = AQM_UNOW;
  269         m_tag_prepend(m, mtag);
  270 
  271         mq_append(&q->mq, m);
  272         update_stats(q, len, 0);
  273         return (0);
  274 
  275 drop:
  276         update_stats(q, 0, 1);
  277         FREE_PKT(m);
  278         return (1);
  279 }
  280 
  281 /* Dequeue a pcaket from queue q */
  282 static struct mbuf * 
  283 aqm_codel_dequeue(struct dn_queue *q)
  284 {
  285         return codel_dequeue(q);
  286 }
  287 
  288 /* 
  289  * initialize Codel for queue 'q' 
  290  * First allocate memory for codel status.
  291  */
  292 static int 
  293 aqm_codel_init(struct dn_queue *q)
  294 {
  295         struct codel_status *cst;
  296 
  297         if (!q->fs->aqmcfg) {
  298                 D("Codel is not configure!d");
  299                 return EINVAL;
  300         }
  301 
  302         q->aqm_status = malloc(sizeof(struct codel_status),
  303                          M_DUMMYNET, M_NOWAIT | M_ZERO);
  304         if (q->aqm_status == NULL) {
  305                 D("Cannot allocate AQM_codel private data");
  306                 return ENOMEM ; 
  307         }
  308 
  309         /* init codel status variables */
  310         cst = q->aqm_status;
  311         cst->dropping=0;
  312         cst->first_above_time=0;
  313         cst->drop_next_time=0;
  314         cst->count=0;
  315         cst->maxpkt_size = 500;
  316 
  317         /* increase reference counters */
  318         codel_desc.ref_count++;
  319 
  320         return 0;
  321 }
  322 
  323 /* 
  324  * Clean up Codel status for queue 'q' 
  325  * Destroy memory allocated for codel status.
  326  */
  327 static int
  328 aqm_codel_cleanup(struct dn_queue *q)
  329 {
  330 
  331         if (q && q->aqm_status) {
  332                 free(q->aqm_status, M_DUMMYNET);
  333                 q->aqm_status = NULL;
  334                 /* decrease reference counters */
  335                 codel_desc.ref_count--;
  336         }
  337         else
  338                 D("Codel already cleaned up");
  339         return 0;
  340 }
  341 
  342 /* 
  343  * Config codel parameters
  344  * also allocate memory for codel configurations
  345  */
  346 static int
  347 aqm_codel_config(struct dn_fsk* fs, struct dn_extra_parms *ep, int len)
  348 {
  349         struct dn_aqm_codel_parms *ccfg;
  350 
  351         int l = sizeof(struct dn_extra_parms);
  352         if (len < l) {
  353                 D("invalid sched parms length got %d need %d", len, l);
  354                 return EINVAL;
  355         }
  356         /* we free the old cfg because maybe the original allocation 
  357          * not the same size as the new one (different AQM type).
  358          */
  359         if (fs->aqmcfg) {
  360                 free(fs->aqmcfg, M_DUMMYNET);
  361                 fs->aqmcfg = NULL;
  362         }
  363 
  364         fs->aqmcfg = malloc(sizeof(struct dn_aqm_codel_parms),
  365                          M_DUMMYNET, M_NOWAIT | M_ZERO);
  366         if (fs->aqmcfg== NULL) {
  367                 D("cannot allocate AQM_codel configuration parameters");
  368                 return ENOMEM; 
  369         }
  370 
  371         /* configure codel parameters */
  372         ccfg = fs->aqmcfg;
  373 
  374         if (ep->par[0] < 0)
  375                 ccfg->target = codel_sysctl.target;
  376         else
  377                 ccfg->target = ep->par[0] * AQM_TIME_1US;
  378 
  379         if (ep->par[1] < 0)
  380                 ccfg->interval = codel_sysctl.interval;
  381         else
  382                 ccfg->interval = ep->par[1] * AQM_TIME_1US;
  383 
  384         if (ep->par[2] < 0)
  385                 ccfg->flags = 0;
  386         else
  387                 ccfg->flags = ep->par[2];
  388 
  389         /* bound codel configurations */
  390         ccfg->target = BOUND_VAR(ccfg->target,1, 5 * AQM_TIME_1S);
  391         ccfg->interval = BOUND_VAR(ccfg->interval,1, 5 * AQM_TIME_1S);
  392         /* increase config reference counter */
  393         codel_desc.cfg_ref_count++;
  394 
  395         return 0;
  396 }
  397 
  398 /*
  399  * Deconfigure Codel and free memory allocation
  400  */
  401 static int
  402 aqm_codel_deconfig(struct dn_fsk* fs)
  403 {
  404 
  405         if (fs && fs->aqmcfg) {
  406                 free(fs->aqmcfg, M_DUMMYNET);
  407                 fs->aqmcfg = NULL;
  408                 fs->aqmfp = NULL;
  409                 /* decrease config reference counter */
  410                 codel_desc.cfg_ref_count--;
  411         }
  412 
  413         return 0;
  414 }
  415 
  416 /* 
  417  * Retrieve Codel configuration parameters.
  418  */ 
  419 static int
  420 aqm_codel_getconfig(struct dn_fsk *fs, struct dn_extra_parms * ep)
  421 {
  422         struct dn_aqm_codel_parms *ccfg;
  423 
  424         if (fs->aqmcfg) {
  425                 strlcpy(ep->name, codel_desc.name, sizeof(ep->name));
  426                 ccfg = fs->aqmcfg;
  427                 ep->par[0] = ccfg->target / AQM_TIME_1US;
  428                 ep->par[1] = ccfg->interval / AQM_TIME_1US;
  429                 ep->par[2] = ccfg->flags;
  430                 return 0;
  431         }
  432         return 1;
  433 }
  434 
  435 static struct dn_aqm codel_desc = {
  436         _SI( .type = )  DN_AQM_CODEL,
  437         _SI( .name = )  "CODEL",
  438         _SI( .enqueue = )  aqm_codel_enqueue,
  439         _SI( .dequeue = )  aqm_codel_dequeue,
  440         _SI( .config = )  aqm_codel_config,
  441         _SI( .getconfig = )  aqm_codel_getconfig,
  442         _SI( .deconfig = )  aqm_codel_deconfig,
  443         _SI( .init = )  aqm_codel_init,
  444         _SI( .cleanup = )  aqm_codel_cleanup,
  445 };
  446 
  447 DECLARE_DNAQM_MODULE(dn_aqm_codel, &codel_desc);
  448 
  449 #endif

Cache object: 33adf157090430293cff7973c0a4c064


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.