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/netgraph/ng_car.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  * Copyright (c) 2005 Nuno Antunes <nuno.antunes@gmail.com>
    3  * Copyright (c) 2007 Alexander Motin <mav@freebsd.org>
    4  * Copyright (c) 2019 Lutz Donnerhacke <lutz@donnerhacke.de>
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  *
   28  * $FreeBSD$
   29  */
   30 
   31 /*
   32  * ng_car - An implementation of committed access rate for netgraph
   33  *
   34  * TODO:
   35  *      - Sanitize input config values (impose some limits)
   36  *      - Implement DSCP marking for IPv4
   37  *      - Decouple functionality into a simple classifier (g/y/r)
   38  *        and various action nodes (i.e. shape, dcsp, pcp)
   39  */
   40 
   41 #include <sys/param.h>
   42 #include <sys/errno.h>
   43 #include <sys/kernel.h>
   44 #include <sys/malloc.h>
   45 #include <sys/mbuf.h>
   46 
   47 #include <netgraph/ng_message.h>
   48 #include <netgraph/ng_parse.h>
   49 #include <netgraph/netgraph.h>
   50 #include <netgraph/ng_car.h>
   51 
   52 #include "qos.h"
   53 
   54 #define NG_CAR_QUEUE_SIZE       100     /* Maximum queue size for SHAPE mode */
   55 #define NG_CAR_QUEUE_MIN_TH     8       /* Minimum RED threshold for SHAPE mode */
   56 
   57 /* Hook private info */
   58 struct hookinfo {
   59         hook_p          hook;           /* this (source) hook */
   60         hook_p          dest;           /* destination hook */
   61 
   62         int64_t         tc;             /* committed token bucket counter */
   63         int64_t         te;             /* exceeded/peak token bucket counter */
   64         struct bintime  lastRefill;     /* last token refill time */
   65 
   66         struct ng_car_hookconf conf;    /* hook configuration */
   67         struct ng_car_hookstats stats;  /* hook stats */
   68 
   69         struct mbuf     *q[NG_CAR_QUEUE_SIZE];  /* circular packet queue */
   70         u_int           q_first;        /* first queue element */
   71         u_int           q_last;         /* last queue element */
   72         struct callout  q_callout;      /* periodic queue processing routine */
   73         struct mtx      q_mtx;          /* queue mutex */
   74 };
   75 
   76 /* Private information for each node instance */
   77 struct privdata {
   78         node_p node;                            /* the node itself */
   79         struct hookinfo upper;                  /* hook to upper layers */
   80         struct hookinfo lower;                  /* hook to lower layers */
   81 };
   82 typedef struct privdata *priv_p;
   83 
   84 static ng_constructor_t ng_car_constructor;
   85 static ng_rcvmsg_t      ng_car_rcvmsg;
   86 static ng_shutdown_t    ng_car_shutdown;
   87 static ng_newhook_t     ng_car_newhook;
   88 static ng_rcvdata_t     ng_car_rcvdata;
   89 static ng_disconnect_t  ng_car_disconnect;
   90 
   91 static void     ng_car_refillhook(struct hookinfo *h);
   92 static void     ng_car_schedule(struct hookinfo *h);
   93 void            ng_car_q_event(node_p node, hook_p hook, void *arg, int arg2);
   94 static void     ng_car_enqueue(struct hookinfo *h, item_p item);
   95 
   96 /* Parse type for struct ng_car_hookstats */
   97 static const struct ng_parse_struct_field ng_car_hookstats_type_fields[]
   98         = NG_CAR_HOOKSTATS;
   99 static const struct ng_parse_type ng_car_hookstats_type = {
  100         &ng_parse_struct_type,
  101         &ng_car_hookstats_type_fields
  102 };
  103 
  104 /* Parse type for struct ng_car_bulkstats */
  105 static const struct ng_parse_struct_field ng_car_bulkstats_type_fields[]
  106         = NG_CAR_BULKSTATS(&ng_car_hookstats_type);
  107 static const struct ng_parse_type ng_car_bulkstats_type = {
  108         &ng_parse_struct_type,
  109         &ng_car_bulkstats_type_fields
  110 };
  111 
  112 /* Parse type for struct ng_car_hookconf */
  113 static const struct ng_parse_struct_field ng_car_hookconf_type_fields[]
  114         = NG_CAR_HOOKCONF;
  115 static const struct ng_parse_type ng_car_hookconf_type = {
  116         &ng_parse_struct_type,
  117         &ng_car_hookconf_type_fields
  118 };
  119 
  120 /* Parse type for struct ng_car_bulkconf */
  121 static const struct ng_parse_struct_field ng_car_bulkconf_type_fields[]
  122         = NG_CAR_BULKCONF(&ng_car_hookconf_type);
  123 static const struct ng_parse_type ng_car_bulkconf_type = {
  124         &ng_parse_struct_type,
  125         &ng_car_bulkconf_type_fields
  126 };
  127 
  128 /* Command list */
  129 static struct ng_cmdlist ng_car_cmdlist[] = {
  130         {
  131           NGM_CAR_COOKIE,
  132           NGM_CAR_GET_STATS,
  133           "getstats",
  134           NULL,
  135           &ng_car_bulkstats_type,
  136         },
  137         {
  138           NGM_CAR_COOKIE,
  139           NGM_CAR_CLR_STATS,
  140           "clrstats",
  141           NULL,
  142           NULL,
  143         },
  144         {
  145           NGM_CAR_COOKIE,
  146           NGM_CAR_GETCLR_STATS,
  147           "getclrstats",
  148           NULL,
  149           &ng_car_bulkstats_type,
  150         },
  151 
  152         {
  153           NGM_CAR_COOKIE,
  154           NGM_CAR_GET_CONF,
  155           "getconf",
  156           NULL,
  157           &ng_car_bulkconf_type,
  158         },
  159         {
  160           NGM_CAR_COOKIE,
  161           NGM_CAR_SET_CONF,
  162           "setconf",
  163           &ng_car_bulkconf_type,
  164           NULL,
  165         },
  166         { 0 }
  167 };
  168 
  169 /* Netgraph node type descriptor */
  170 static struct ng_type ng_car_typestruct = {
  171         .version =      NG_ABI_VERSION,
  172         .name =         NG_CAR_NODE_TYPE,
  173         .constructor =  ng_car_constructor,
  174         .rcvmsg =       ng_car_rcvmsg,
  175         .shutdown =     ng_car_shutdown,
  176         .newhook =      ng_car_newhook,
  177         .rcvdata =      ng_car_rcvdata,
  178         .disconnect =   ng_car_disconnect,
  179         .cmdlist =      ng_car_cmdlist,
  180 };
  181 NETGRAPH_INIT(car, &ng_car_typestruct);
  182 
  183 /*
  184  * Node constructor
  185  */
  186 static int
  187 ng_car_constructor(node_p node)
  188 {
  189         priv_p priv;
  190 
  191         /* Initialize private descriptor. */
  192         priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
  193 
  194         NG_NODE_SET_PRIVATE(node, priv);
  195         priv->node = node;
  196 
  197         /*
  198          * Arbitrary default values
  199          */
  200 
  201         priv->upper.hook = NULL;
  202         priv->upper.dest = NULL;
  203         priv->upper.tc = priv->upper.conf.cbs = NG_CAR_CBS_MIN;
  204         priv->upper.te = priv->upper.conf.ebs = NG_CAR_EBS_MIN;
  205         priv->upper.conf.cir = NG_CAR_CIR_DFLT;
  206         priv->upper.conf.green_action = NG_CAR_ACTION_FORWARD;
  207         priv->upper.conf.yellow_action = NG_CAR_ACTION_FORWARD;
  208         priv->upper.conf.red_action = NG_CAR_ACTION_DROP;
  209         priv->upper.conf.mode = 0;
  210         getbinuptime(&priv->upper.lastRefill);
  211         priv->upper.q_first = 0;
  212         priv->upper.q_last = 0;
  213         ng_callout_init(&priv->upper.q_callout);
  214         mtx_init(&priv->upper.q_mtx, "ng_car_u", NULL, MTX_DEF);
  215 
  216         priv->lower.hook = NULL;
  217         priv->lower.dest = NULL;
  218         priv->lower.tc = priv->lower.conf.cbs = NG_CAR_CBS_MIN;
  219         priv->lower.te = priv->lower.conf.ebs = NG_CAR_EBS_MIN;
  220         priv->lower.conf.cir = NG_CAR_CIR_DFLT;
  221         priv->lower.conf.green_action = NG_CAR_ACTION_FORWARD;
  222         priv->lower.conf.yellow_action = NG_CAR_ACTION_FORWARD;
  223         priv->lower.conf.red_action = NG_CAR_ACTION_DROP;
  224         priv->lower.conf.mode = 0;
  225         priv->lower.lastRefill = priv->upper.lastRefill;
  226         priv->lower.q_first = 0;
  227         priv->lower.q_last = 0;
  228         ng_callout_init(&priv->lower.q_callout);
  229         mtx_init(&priv->lower.q_mtx, "ng_car_l", NULL, MTX_DEF);
  230 
  231         return (0);
  232 }
  233 
  234 /*
  235  * Add a hook.
  236  */
  237 static int
  238 ng_car_newhook(node_p node, hook_p hook, const char *name)
  239 {
  240         const priv_p priv = NG_NODE_PRIVATE(node);
  241 
  242         if (strcmp(name, NG_CAR_HOOK_LOWER) == 0) {
  243                 priv->lower.hook = hook;
  244                 priv->upper.dest = hook;
  245                 bzero(&priv->lower.stats, sizeof(priv->lower.stats));
  246                 NG_HOOK_SET_PRIVATE(hook, &priv->lower);
  247         } else if (strcmp(name, NG_CAR_HOOK_UPPER) == 0) {
  248                 priv->upper.hook = hook;
  249                 priv->lower.dest = hook;
  250                 bzero(&priv->upper.stats, sizeof(priv->upper.stats));
  251                 NG_HOOK_SET_PRIVATE(hook, &priv->upper);
  252         } else
  253                 return (EINVAL);
  254         return(0);
  255 }
  256 
  257 /*
  258  * Data has arrived.
  259  */
  260 static int
  261 ng_car_rcvdata(hook_p hook, item_p item )
  262 {
  263         struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook);
  264         struct mbuf *m;
  265         struct m_qos_color *colp;
  266         enum qos_color col;
  267         int error = 0;
  268         u_int len;
  269 
  270         /* If queue is not empty now then enqueue packet. */
  271         if (hinfo->q_first != hinfo->q_last) {
  272                 ng_car_enqueue(hinfo, item);
  273                 return (0);
  274         }
  275 
  276         m = NGI_M(item);
  277 
  278 #define NG_CAR_PERFORM_MATCH_ACTION(a,col)                      \
  279         do {                                            \
  280                 switch (a) {                            \
  281                 case NG_CAR_ACTION_FORWARD:             \
  282                         /* Do nothing. */               \
  283                         break;                          \
  284                 case NG_CAR_ACTION_MARK:                \
  285                         if (colp == NULL) {             \
  286                                 colp = (void *)m_tag_alloc(             \
  287                                     M_QOS_COOKIE, M_QOS_COLOR,          \
  288                                     MTAG_SIZE(m_qos_color), M_NOWAIT);  \
  289                                 if (colp != NULL)                       \
  290                                     m_tag_prepend(m, &colp->tag);       \
  291                         }                               \
  292                         if (colp != NULL)               \
  293                             colp->color = col;          \
  294                         break;                          \
  295                 case NG_CAR_ACTION_DROP:                \
  296                 default:                                \
  297                         /* Drop packet and return. */   \
  298                         NG_FREE_ITEM(item);             \
  299                         ++hinfo->stats.droped_pkts;     \
  300                         return (0);                     \
  301                 }                                       \
  302         } while (0)
  303 
  304         /* Packet is counted as 128 tokens for better resolution */
  305         if (hinfo->conf.opt & NG_CAR_COUNT_PACKETS) {
  306                 len = 128;
  307         } else {
  308                 len = m->m_pkthdr.len;
  309         }
  310 
  311         /* Determine current color of the packet (default green) */
  312         colp = (void *)m_tag_locate(m, M_QOS_COOKIE, M_QOS_COLOR, NULL);
  313         if ((hinfo->conf.opt & NG_CAR_COLOR_AWARE) && (colp != NULL))
  314             col = colp->color;
  315         else
  316             col = QOS_COLOR_GREEN;
  317 
  318         /* Check committed token bucket. */
  319         if (hinfo->tc - len >= 0 && col <= QOS_COLOR_GREEN) {
  320                 /* This packet is green. */
  321                 ++hinfo->stats.green_pkts;
  322                 hinfo->tc -= len;
  323                 NG_CAR_PERFORM_MATCH_ACTION(
  324                     hinfo->conf.green_action,
  325                     QOS_COLOR_GREEN);
  326         } else {
  327 
  328                 /* Refill only if not green without it. */
  329                 ng_car_refillhook(hinfo);
  330 
  331                  /* Check committed token bucket again after refill. */
  332                 if (hinfo->tc - len >= 0 && col <= QOS_COLOR_GREEN) {
  333                         /* This packet is green */
  334                         ++hinfo->stats.green_pkts;
  335                         hinfo->tc -= len;
  336                         NG_CAR_PERFORM_MATCH_ACTION(
  337                             hinfo->conf.green_action,
  338                             QOS_COLOR_GREEN);
  339 
  340                 /* If not green and mode is SHAPE, enqueue packet. */
  341                 } else if (hinfo->conf.mode == NG_CAR_SHAPE) {
  342                         ng_car_enqueue(hinfo, item);
  343                         return (0);
  344 
  345                 /* If not green and mode is RED, calculate probability. */
  346                 } else if (hinfo->conf.mode == NG_CAR_RED) {
  347                         /* Is packet is bigger then extended burst? */
  348                         if (len - (hinfo->tc - len) > hinfo->conf.ebs ||
  349                             col >= QOS_COLOR_RED) {
  350                                 /* This packet is definitely red. */
  351                                 ++hinfo->stats.red_pkts;
  352                                 hinfo->te = 0;
  353                                 NG_CAR_PERFORM_MATCH_ACTION(
  354                                         hinfo->conf.red_action,
  355                                         QOS_COLOR_RED);
  356 
  357                         /* Use token bucket to simulate RED-like drop
  358                            probability. */
  359                         } else if (hinfo->te + (len - hinfo->tc) < hinfo->conf.ebs &&
  360                                    col <= QOS_COLOR_YELLOW) {
  361                                 /* This packet is yellow */
  362                                 ++hinfo->stats.yellow_pkts;
  363                                 hinfo->te += len - hinfo->tc;
  364                                 /* Go to negative tokens. */
  365                                 hinfo->tc -= len;
  366                                 NG_CAR_PERFORM_MATCH_ACTION(
  367                                     hinfo->conf.yellow_action,
  368                                     QOS_COLOR_YELLOW);
  369                         } else {
  370                                 /* This packet is probably red. */
  371                                 ++hinfo->stats.red_pkts;
  372                                 hinfo->te = 0;
  373                                 NG_CAR_PERFORM_MATCH_ACTION(
  374                                     hinfo->conf.red_action,
  375                                     QOS_COLOR_RED);
  376                         }
  377                 /* If not green and mode is SINGLE/DOUBLE RATE. */
  378                 } else {
  379                         /* Check extended token bucket. */
  380                         if (hinfo->te - len >= 0 && col <= QOS_COLOR_YELLOW) {
  381                                 /* This packet is yellow */
  382                                 ++hinfo->stats.yellow_pkts;
  383                                 hinfo->te -= len;
  384                                 NG_CAR_PERFORM_MATCH_ACTION(
  385                                     hinfo->conf.yellow_action,
  386                                     QOS_COLOR_YELLOW);
  387                         } else {
  388                                 /* This packet is red */
  389                                 ++hinfo->stats.red_pkts;
  390                                 NG_CAR_PERFORM_MATCH_ACTION(
  391                                     hinfo->conf.red_action,
  392                                     QOS_COLOR_RED);
  393                         }
  394                 }
  395         }
  396 
  397 #undef NG_CAR_PERFORM_MATCH_ACTION
  398 
  399         NG_FWD_ITEM_HOOK(error, item, hinfo->dest);
  400         if (error != 0)
  401                 ++hinfo->stats.errors;
  402         ++hinfo->stats.passed_pkts;
  403 
  404         return (error);
  405 }
  406 
  407 /*
  408  * Receive a control message.
  409  */
  410 static int
  411 ng_car_rcvmsg(node_p node, item_p item, hook_p lasthook)
  412 {
  413         const priv_p priv = NG_NODE_PRIVATE(node);
  414         struct ng_mesg *resp = NULL;
  415         int error = 0;
  416         struct ng_mesg *msg;
  417 
  418         NGI_GET_MSG(item, msg);
  419         switch (msg->header.typecookie) {
  420         case NGM_CAR_COOKIE:
  421                 switch (msg->header.cmd) {
  422                 case NGM_CAR_GET_STATS:
  423                 case NGM_CAR_GETCLR_STATS:
  424                         {
  425                                 struct ng_car_bulkstats *bstats;
  426 
  427                                 NG_MKRESPONSE(resp, msg,
  428                                         sizeof(*bstats), M_NOWAIT);
  429                                 if (resp == NULL) {
  430                                         error = ENOMEM;
  431                                         break;
  432                                 }
  433                                 bstats = (struct ng_car_bulkstats *)resp->data;
  434 
  435                                 bcopy(&priv->upper.stats, &bstats->downstream,
  436                                     sizeof(bstats->downstream));
  437                                 bcopy(&priv->lower.stats, &bstats->upstream,
  438                                     sizeof(bstats->upstream));
  439                         }
  440                         if (msg->header.cmd == NGM_CAR_GET_STATS)
  441                                 break;
  442                 case NGM_CAR_CLR_STATS:
  443                         bzero(&priv->upper.stats,
  444                                 sizeof(priv->upper.stats));
  445                         bzero(&priv->lower.stats,
  446                                 sizeof(priv->lower.stats));
  447                         break;
  448                 case NGM_CAR_GET_CONF:
  449                         {
  450                                 struct ng_car_bulkconf *bconf;
  451 
  452                                 NG_MKRESPONSE(resp, msg,
  453                                         sizeof(*bconf), M_NOWAIT);
  454                                 if (resp == NULL) {
  455                                         error = ENOMEM;
  456                                         break;
  457                                 }
  458                                 bconf = (struct ng_car_bulkconf *)resp->data;
  459 
  460                                 bcopy(&priv->upper.conf, &bconf->downstream,
  461                                     sizeof(bconf->downstream));
  462                                 bcopy(&priv->lower.conf, &bconf->upstream,
  463                                     sizeof(bconf->upstream));
  464                                 /* Convert internal 1/(8*128) of pps into pps */
  465                                 if (bconf->downstream.opt & NG_CAR_COUNT_PACKETS) {
  466                                     bconf->downstream.cir /= 1024;
  467                                     bconf->downstream.pir /= 1024;
  468                                     bconf->downstream.cbs /= 128;
  469                                     bconf->downstream.ebs /= 128;
  470                                 }
  471                                 if (bconf->upstream.opt & NG_CAR_COUNT_PACKETS) {
  472                                     bconf->upstream.cir /= 1024;
  473                                     bconf->upstream.pir /= 1024;
  474                                     bconf->upstream.cbs /= 128;
  475                                     bconf->upstream.ebs /= 128;
  476                                 }
  477                         }
  478                         break;
  479                 case NGM_CAR_SET_CONF:
  480                         {
  481                                 struct ng_car_bulkconf *const bconf =
  482                                 (struct ng_car_bulkconf *)msg->data;
  483 
  484                                 /* Check for invalid or illegal config. */
  485                                 if (msg->header.arglen != sizeof(*bconf)) {
  486                                         error = EINVAL;
  487                                         break;
  488                                 }
  489                                 /* Convert pps into internal 1/(8*128) of pps */
  490                                 if (bconf->downstream.opt & NG_CAR_COUNT_PACKETS) {
  491                                     bconf->downstream.cir *= 1024;
  492                                     bconf->downstream.pir *= 1024;
  493                                     bconf->downstream.cbs *= 125;
  494                                     bconf->downstream.ebs *= 125;
  495                                 }
  496                                 if (bconf->upstream.opt & NG_CAR_COUNT_PACKETS) {
  497                                     bconf->upstream.cir *= 1024;
  498                                     bconf->upstream.pir *= 1024;
  499                                     bconf->upstream.cbs *= 125;
  500                                     bconf->upstream.ebs *= 125;
  501                                 }
  502                                 if ((bconf->downstream.cir > 1000000000) ||
  503                                     (bconf->downstream.pir > 1000000000) ||
  504                                     (bconf->upstream.cir > 1000000000) ||
  505                                     (bconf->upstream.pir > 1000000000) ||
  506                                     (bconf->downstream.cbs == 0 &&
  507                                         bconf->downstream.ebs == 0) ||
  508                                     (bconf->upstream.cbs == 0 &&
  509                                         bconf->upstream.ebs == 0))
  510                                 {
  511                                         error = EINVAL;
  512                                         break;
  513                                 }
  514                                 if ((bconf->upstream.mode == NG_CAR_SHAPE) &&
  515                                     (bconf->upstream.cir == 0)) {
  516                                         error = EINVAL;
  517                                         break;
  518                                 }
  519                                 if ((bconf->downstream.mode == NG_CAR_SHAPE) &&
  520                                     (bconf->downstream.cir == 0)) {
  521                                         error = EINVAL;
  522                                         break;
  523                                 }
  524 
  525                                 /* Copy downstream config. */
  526                                 bcopy(&bconf->downstream, &priv->upper.conf,
  527                                     sizeof(priv->upper.conf));
  528                                 priv->upper.tc = priv->upper.conf.cbs;
  529                                 if (priv->upper.conf.mode == NG_CAR_RED ||
  530                                     priv->upper.conf.mode == NG_CAR_SHAPE) {
  531                                         priv->upper.te = 0;
  532                                 } else {
  533                                         priv->upper.te = priv->upper.conf.ebs;
  534                                 }
  535 
  536                                 /* Copy upstream config. */
  537                                 bcopy(&bconf->upstream, &priv->lower.conf,
  538                                     sizeof(priv->lower.conf));
  539                                 priv->lower.tc = priv->lower.conf.cbs;
  540                                 if (priv->lower.conf.mode == NG_CAR_RED ||
  541                                     priv->lower.conf.mode == NG_CAR_SHAPE) {
  542                                         priv->lower.te = 0;
  543                                 } else {
  544                                         priv->lower.te = priv->lower.conf.ebs;
  545                                 }
  546                         }
  547                         break;
  548                 default:
  549                         error = EINVAL;
  550                         break;
  551                 }
  552                 break;
  553         default:
  554                 error = EINVAL;
  555                 break;
  556         }
  557         NG_RESPOND_MSG(error, node, item, resp);
  558         NG_FREE_MSG(msg);
  559         return (error);
  560 }
  561 
  562 /*
  563  * Do local shutdown processing.
  564  */
  565 static int
  566 ng_car_shutdown(node_p node)
  567 {
  568         const priv_p priv = NG_NODE_PRIVATE(node);
  569 
  570         ng_uncallout(&priv->upper.q_callout, node);
  571         ng_uncallout(&priv->lower.q_callout, node);
  572         mtx_destroy(&priv->upper.q_mtx);
  573         mtx_destroy(&priv->lower.q_mtx);
  574         NG_NODE_UNREF(priv->node);
  575         free(priv, M_NETGRAPH);
  576         return (0);
  577 }
  578 
  579 /*
  580  * Hook disconnection.
  581  *
  582  * For this type, removal of the last link destroys the node.
  583  */
  584 static int
  585 ng_car_disconnect(hook_p hook)
  586 {
  587         struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook);
  588         const node_p node = NG_HOOK_NODE(hook);
  589         const priv_p priv = NG_NODE_PRIVATE(node);
  590 
  591         if (hinfo) {
  592                 /* Purge queue if not empty. */
  593                 while (hinfo->q_first != hinfo->q_last) {
  594                         NG_FREE_M(hinfo->q[hinfo->q_first]);
  595                         hinfo->q_first++;
  596                         if (hinfo->q_first >= NG_CAR_QUEUE_SIZE)
  597                                 hinfo->q_first = 0;
  598                 }
  599                 /* Remove hook refs. */
  600                 if (hinfo->hook == priv->upper.hook)
  601                         priv->lower.dest = NULL;
  602                 else
  603                         priv->upper.dest = NULL;
  604                 hinfo->hook = NULL;
  605         }
  606         /* Already shutting down? */
  607         if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
  608             && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
  609                 ng_rmnode_self(NG_HOOK_NODE(hook));
  610         return (0);
  611 }
  612 
  613 /*
  614  * Hook's token buckets refillment.
  615  */
  616 static void
  617 ng_car_refillhook(struct hookinfo *h)
  618 {
  619         struct bintime newt, deltat;
  620         unsigned int deltat_us;
  621 
  622         /* Get current time. */
  623         getbinuptime(&newt);
  624 
  625         /* Get time delta since last refill. */
  626         deltat = newt;
  627         bintime_sub(&deltat, &h->lastRefill);
  628 
  629         /* Time must go forward. */
  630         if (deltat.sec < 0) {
  631             h->lastRefill = newt;
  632             return;
  633         }
  634 
  635         /* But not too far forward. */
  636         if (deltat.sec >= 1000) {
  637             deltat_us = (1000 << 20);
  638         } else {
  639             /* convert bintime to the 1/(2^20) of sec */
  640             deltat_us = (deltat.sec << 20) + (deltat.frac >> 44);
  641         }
  642 
  643         if (h->conf.mode == NG_CAR_SINGLE_RATE) {
  644                 int64_t delta;
  645                 /* Refill committed token bucket. */
  646                 h->tc += (h->conf.cir * deltat_us) >> 23;
  647                 delta = h->tc - h->conf.cbs;
  648                 if (delta > 0) {
  649                         h->tc = h->conf.cbs;
  650 
  651                         /* Refill exceeded token bucket. */
  652                         h->te += delta;
  653                         if (h->te > ((int64_t)h->conf.ebs))
  654                                 h->te = h->conf.ebs;
  655                 }
  656 
  657         } else if (h->conf.mode == NG_CAR_DOUBLE_RATE) {
  658                 /* Refill committed token bucket. */
  659                 h->tc += (h->conf.cir * deltat_us) >> 23;
  660                 if (h->tc > ((int64_t)h->conf.cbs))
  661                         h->tc = h->conf.cbs;
  662 
  663                 /* Refill peak token bucket. */
  664                 h->te += (h->conf.pir * deltat_us) >> 23;
  665                 if (h->te > ((int64_t)h->conf.ebs))
  666                         h->te = h->conf.ebs;
  667 
  668         } else { /* RED or SHAPE mode. */
  669                 /* Refill committed token bucket. */
  670                 h->tc += (h->conf.cir * deltat_us) >> 23;
  671                 if (h->tc > ((int64_t)h->conf.cbs))
  672                         h->tc = h->conf.cbs;
  673         }
  674 
  675         /* Remember this moment. */
  676         h->lastRefill = newt;
  677 }
  678 
  679 /*
  680  * Schedule callout when we will have required tokens.
  681  */
  682 static void
  683 ng_car_schedule(struct hookinfo *hinfo)
  684 {
  685         int     delay;
  686 
  687         delay = (-(hinfo->tc)) * hz * 8 / hinfo->conf.cir + 1;
  688 
  689         ng_callout(&hinfo->q_callout, NG_HOOK_NODE(hinfo->hook), hinfo->hook,
  690             delay, &ng_car_q_event, NULL, 0);
  691 }
  692 
  693 /*
  694  * Queue processing callout handler.
  695  */
  696 void
  697 ng_car_q_event(node_p node, hook_p hook, void *arg, int arg2)
  698 {
  699         struct hookinfo *hinfo = NG_HOOK_PRIVATE(hook);
  700         struct mbuf     *m;
  701         int             error;
  702 
  703         /* Refill tokens for time we have slept. */
  704         ng_car_refillhook(hinfo);
  705 
  706         /* If we have some tokens */
  707         while (hinfo->tc >= 0) {
  708 
  709                 /* Send packet. */
  710                 m = hinfo->q[hinfo->q_first];
  711                 NG_SEND_DATA_ONLY(error, hinfo->dest, m);
  712                 if (error != 0)
  713                         ++hinfo->stats.errors;
  714                 ++hinfo->stats.passed_pkts;
  715 
  716                 /* Get next one. */
  717                 hinfo->q_first++;
  718                 if (hinfo->q_first >= NG_CAR_QUEUE_SIZE)
  719                         hinfo->q_first = 0;
  720 
  721                 /* Stop if none left. */
  722                 if (hinfo->q_first == hinfo->q_last)
  723                         break;
  724 
  725                 /* If we have more packet, try it. */
  726                 m = hinfo->q[hinfo->q_first];
  727                 if (hinfo->conf.opt & NG_CAR_COUNT_PACKETS) {
  728                         hinfo->tc -= 128;
  729                 } else {
  730                         hinfo->tc -= m->m_pkthdr.len;
  731                 }
  732         }
  733 
  734         /* If something left */
  735         if (hinfo->q_first != hinfo->q_last)
  736                 /* Schedule queue processing. */
  737                 ng_car_schedule(hinfo);
  738 }
  739 
  740 /*
  741  * Enqueue packet.
  742  */
  743 static void
  744 ng_car_enqueue(struct hookinfo *hinfo, item_p item)
  745 {
  746         struct mbuf        *m;
  747         int                len;
  748         struct m_qos_color *colp;
  749         enum qos_color     col;
  750 
  751         NGI_GET_M(item, m);
  752         NG_FREE_ITEM(item);
  753 
  754         /* Determine current color of the packet (default green) */
  755         colp = (void *)m_tag_locate(m, M_QOS_COOKIE, M_QOS_COLOR, NULL);
  756         if ((hinfo->conf.opt & NG_CAR_COLOR_AWARE) && (colp != NULL))
  757             col = colp->color;
  758         else
  759             col = QOS_COLOR_GREEN;
  760 
  761         /* Lock queue mutex. */
  762         mtx_lock(&hinfo->q_mtx);
  763 
  764         /* Calculate used queue length. */
  765         len = hinfo->q_last - hinfo->q_first;
  766         if (len < 0)
  767                 len += NG_CAR_QUEUE_SIZE;
  768 
  769         /* If queue is overflowed or we have no RED tokens. */
  770         if ((len >= (NG_CAR_QUEUE_SIZE - 1)) ||
  771             (hinfo->te + len >= NG_CAR_QUEUE_SIZE) ||
  772             (col >= QOS_COLOR_RED)) {
  773                 /* Drop packet. */
  774                 ++hinfo->stats.red_pkts;
  775                 ++hinfo->stats.droped_pkts;
  776                 NG_FREE_M(m);
  777 
  778                 hinfo->te = 0;
  779         } else {
  780                 /* This packet is yellow. */
  781                 ++hinfo->stats.yellow_pkts;
  782 
  783                 /* Enqueue packet. */
  784                 hinfo->q[hinfo->q_last] = m;
  785                 hinfo->q_last++;
  786                 if (hinfo->q_last >= NG_CAR_QUEUE_SIZE)
  787                         hinfo->q_last = 0;
  788 
  789                 /* Use RED tokens. */
  790                 if (len > NG_CAR_QUEUE_MIN_TH)
  791                         hinfo->te += len - NG_CAR_QUEUE_MIN_TH;
  792 
  793                 /* If this is a first packet in the queue. */
  794                 if (len == 0) {
  795                         if (hinfo->conf.opt & NG_CAR_COUNT_PACKETS) {
  796                                 hinfo->tc -= 128;
  797                         } else {
  798                                 hinfo->tc -= m->m_pkthdr.len;
  799                         }
  800 
  801                         /* Schedule queue processing. */
  802                         ng_car_schedule(hinfo);
  803                 }
  804         }
  805 
  806         /* Unlock queue mutex. */
  807         mtx_unlock(&hinfo->q_mtx);
  808 }

Cache object: c013a2054e4e2389d769e3fa6903f8e2


[ 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.