root/altq/altq_cbq.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cbq_class_destroy
  2. clh_to_clp
  3. cbq_clear_interface
  4. cbq_request
  5. get_class_stats
  6. cbq_pfattach
  7. cbq_add_altq
  8. cbq_remove_altq
  9. cbq_add_queue
  10. cbq_remove_queue
  11. cbq_getqstats
  12. cbq_enqueue
  13. cbq_dequeue
  14. cbqrestart
  15. cbq_purge

    1 /*      $OpenBSD: altq_cbq.c,v 1.22 2007/05/28 17:16:38 henning Exp $   */
    2 /*      $KAME: altq_cbq.c,v 1.9 2000/12/14 08:12:45 thorpej Exp $       */
    3 
    4 /*
    5  * Copyright (c) Sun Microsystems, Inc. 1993-1998 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  *
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  *
   14  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  *
   18  * 3. All advertising materials mentioning features or use of this software
   19  *    must display the following acknowledgement:
   20  *      This product includes software developed by the SMCC Technology
   21  *      Development Group at Sun Microsystems, Inc.
   22  *
   23  * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or
   24  *      promote products derived from this software without specific prior
   25  *      written permission.
   26  *
   27  * SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE
   28  * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE.  The software is
   29  * provided "as is" without express or implied warranty of any kind.
   30  *
   31  * These notices must be retained in any copies of any part of this software.
   32  */
   33 
   34 #include <sys/param.h>
   35 #include <sys/malloc.h>
   36 #include <sys/mbuf.h>
   37 #include <sys/socket.h>
   38 #include <sys/systm.h>
   39 #include <sys/errno.h>
   40 #include <sys/time.h>
   41 
   42 #include <net/if.h>
   43 #include <netinet/in.h>
   44 
   45 #include <net/pfvar.h>
   46 #include <altq/altq.h>
   47 #include <altq/altq_cbq.h>
   48 
   49 /*
   50  * Forward Declarations.
   51  */
   52 static int               cbq_class_destroy(cbq_state_t *, struct rm_class *);
   53 static struct rm_class  *clh_to_clp(cbq_state_t *, u_int32_t);
   54 static int               cbq_clear_interface(cbq_state_t *);
   55 static int               cbq_request(struct ifaltq *, int, void *);
   56 static int               cbq_enqueue(struct ifaltq *, struct mbuf *,
   57                              struct altq_pktattr *);
   58 static struct mbuf      *cbq_dequeue(struct ifaltq *, int);
   59 static void              cbqrestart(struct ifaltq *);
   60 static void              get_class_stats(class_stats_t *, struct rm_class *);
   61 static void              cbq_purge(cbq_state_t *);
   62 
   63 /*
   64  * int
   65  * cbq_class_destroy(cbq_mod_state_t *, struct rm_class *) - This
   66  *      function destroys a given traffic class.  Before destroying
   67  *      the class, all traffic for that class is released.
   68  */
   69 static int
   70 cbq_class_destroy(cbq_state_t *cbqp, struct rm_class *cl)
   71 {
   72         int     i;
   73 
   74         /* delete the class */
   75         rmc_delete_class(&cbqp->ifnp, cl);
   76 
   77         /*
   78          * free the class handle
   79          */
   80         for (i = 0; i < CBQ_MAX_CLASSES; i++)
   81                 if (cbqp->cbq_class_tbl[i] == cl)
   82                         cbqp->cbq_class_tbl[i] = NULL;
   83 
   84         if (cl == cbqp->ifnp.root_)
   85                 cbqp->ifnp.root_ = NULL;
   86         if (cl == cbqp->ifnp.default_)
   87                 cbqp->ifnp.default_ = NULL;
   88         return (0);
   89 }
   90 
   91 /* convert class handle to class pointer */
   92 static struct rm_class *
   93 clh_to_clp(cbq_state_t *cbqp, u_int32_t chandle)
   94 {
   95         int i;
   96         struct rm_class *cl;
   97 
   98         if (chandle == 0)
   99                 return (NULL);
  100         /*
  101          * first, try the slot corresponding to the lower bits of the handle.
  102          * if it does not match, do the linear table search.
  103          */
  104         i = chandle % CBQ_MAX_CLASSES;
  105         if ((cl = cbqp->cbq_class_tbl[i]) != NULL &&
  106             cl->stats_.handle == chandle)
  107                 return (cl);
  108         for (i = 0; i < CBQ_MAX_CLASSES; i++)
  109                 if ((cl = cbqp->cbq_class_tbl[i]) != NULL &&
  110                     cl->stats_.handle == chandle)
  111                         return (cl);
  112         return (NULL);
  113 }
  114 
  115 static int
  116 cbq_clear_interface(cbq_state_t *cbqp)
  117 {
  118         int              again, i;
  119         struct rm_class *cl;
  120 
  121         /* clear out the classes now */
  122         do {
  123                 again = 0;
  124                 for (i = 0; i < CBQ_MAX_CLASSES; i++) {
  125                         if ((cl = cbqp->cbq_class_tbl[i]) != NULL) {
  126                                 if (is_a_parent_class(cl))
  127                                         again++;
  128                                 else {
  129                                         cbq_class_destroy(cbqp, cl);
  130                                         cbqp->cbq_class_tbl[i] = NULL;
  131                                         if (cl == cbqp->ifnp.root_)
  132                                                 cbqp->ifnp.root_ = NULL;
  133                                         if (cl == cbqp->ifnp.default_)
  134                                                 cbqp->ifnp.default_ = NULL;
  135                                 }
  136                         }
  137                 }
  138         } while (again);
  139 
  140         return (0);
  141 }
  142 
  143 static int
  144 cbq_request(struct ifaltq *ifq, int req, void *arg)
  145 {
  146         cbq_state_t     *cbqp = (cbq_state_t *)ifq->altq_disc;
  147 
  148         switch (req) {
  149         case ALTRQ_PURGE:
  150                 cbq_purge(cbqp);
  151                 break;
  152         }
  153         return (0);
  154 }
  155 
  156 /* copy the stats info in rm_class to class_states_t */
  157 static void
  158 get_class_stats(class_stats_t *statsp, struct rm_class *cl)
  159 {
  160         statsp->handle          = cl->stats_.handle;
  161         statsp->xmit_cnt        = cl->stats_.xmit_cnt;
  162         statsp->drop_cnt        = cl->stats_.drop_cnt;
  163         statsp->over            = cl->stats_.over;
  164         statsp->borrows         = cl->stats_.borrows;
  165         statsp->overactions     = cl->stats_.overactions;
  166         statsp->delays          = cl->stats_.delays;
  167 
  168         statsp->depth           = cl->depth_;
  169         statsp->priority        = cl->pri_;
  170         statsp->maxidle         = cl->maxidle_;
  171         statsp->minidle         = cl->minidle_;
  172         statsp->offtime         = cl->offtime_;
  173         statsp->qmax            = qlimit(cl->q_);
  174         statsp->ns_per_byte     = cl->ns_per_byte_;
  175         statsp->wrr_allot       = cl->w_allotment_;
  176         statsp->qcnt            = qlen(cl->q_);
  177         statsp->avgidle         = cl->avgidle_;
  178 
  179         statsp->qtype           = qtype(cl->q_);
  180 #ifdef ALTQ_RED
  181         if (q_is_red(cl->q_))
  182                 red_getstats(cl->red_, &statsp->red[0]);
  183 #endif
  184 #ifdef ALTQ_RIO
  185         if (q_is_rio(cl->q_))
  186                 rio_getstats((rio_t *)cl->red_, &statsp->red[0]);
  187 #endif
  188 }
  189 
  190 int
  191 cbq_pfattach(struct pf_altq *a)
  192 {
  193         struct ifnet    *ifp;
  194         int              s, error;
  195 
  196         if ((ifp = ifunit(a->ifname)) == NULL || a->altq_disc == NULL)
  197                 return (EINVAL);
  198         s = splnet();
  199         error = altq_attach(&ifp->if_snd, ALTQT_CBQ, a->altq_disc,
  200             cbq_enqueue, cbq_dequeue, cbq_request, NULL, NULL);
  201         splx(s);
  202         return (error);
  203 }
  204 
  205 int
  206 cbq_add_altq(struct pf_altq *a)
  207 {
  208         cbq_state_t     *cbqp;
  209         struct ifnet    *ifp;
  210 
  211         if ((ifp = ifunit(a->ifname)) == NULL)
  212                 return (EINVAL);
  213         if (!ALTQ_IS_READY(&ifp->if_snd))
  214                 return (ENODEV);
  215 
  216         /* allocate and initialize cbq_state_t */
  217         MALLOC(cbqp, cbq_state_t *, sizeof(cbq_state_t), M_DEVBUF, M_WAITOK);
  218         if (cbqp == NULL)
  219                 return (ENOMEM);
  220         bzero(cbqp, sizeof(cbq_state_t));
  221         CALLOUT_INIT(&cbqp->cbq_callout);
  222         cbqp->cbq_qlen = 0;
  223         cbqp->ifnp.ifq_ = &ifp->if_snd;     /* keep the ifq */
  224 
  225         /* keep the state in pf_altq */
  226         a->altq_disc = cbqp;
  227 
  228         return (0);
  229 }
  230 
  231 int
  232 cbq_remove_altq(struct pf_altq *a)
  233 {
  234         cbq_state_t     *cbqp;
  235 
  236         if ((cbqp = a->altq_disc) == NULL)
  237                 return (EINVAL);
  238         a->altq_disc = NULL;
  239 
  240         cbq_clear_interface(cbqp);
  241 
  242         if (cbqp->ifnp.default_)
  243                 cbq_class_destroy(cbqp, cbqp->ifnp.default_);
  244         if (cbqp->ifnp.root_)
  245                 cbq_class_destroy(cbqp, cbqp->ifnp.root_);
  246 
  247         /* deallocate cbq_state_t */
  248         FREE(cbqp, M_DEVBUF);
  249 
  250         return (0);
  251 }
  252 
  253 int
  254 cbq_add_queue(struct pf_altq *a)
  255 {
  256         struct rm_class *borrow, *parent;
  257         cbq_state_t     *cbqp;
  258         struct rm_class *cl;
  259         struct cbq_opts *opts;
  260         int             i;
  261 
  262         if ((cbqp = a->altq_disc) == NULL)
  263                 return (EINVAL);
  264         if (a->qid == 0)
  265                 return (EINVAL);
  266 
  267         /*
  268          * find a free slot in the class table.  if the slot matching
  269          * the lower bits of qid is free, use this slot.  otherwise,
  270          * use the first free slot.
  271          */
  272         i = a->qid % CBQ_MAX_CLASSES;
  273         if (cbqp->cbq_class_tbl[i] != NULL) {
  274                 for (i = 0; i < CBQ_MAX_CLASSES; i++)
  275                         if (cbqp->cbq_class_tbl[i] == NULL)
  276                                 break;
  277                 if (i == CBQ_MAX_CLASSES)
  278                         return (EINVAL);
  279         }
  280 
  281         opts = &a->pq_u.cbq_opts;
  282         /* check parameters */
  283         if (a->priority >= CBQ_MAXPRI)
  284                 return (EINVAL);
  285 
  286         /* Get pointers to parent and borrow classes.  */
  287         parent = clh_to_clp(cbqp, a->parent_qid);
  288         if (opts->flags & CBQCLF_BORROW)
  289                 borrow = parent;
  290         else
  291                 borrow = NULL;
  292 
  293         /*
  294          * A class must borrow from its parent or it can not
  295          * borrow at all.  Hence, borrow can be null.
  296          */
  297         if (parent == NULL && (opts->flags & CBQCLF_ROOTCLASS) == 0) {
  298                 printf("cbq_add_queue: no parent class!\n");
  299                 return (EINVAL);
  300         }
  301 
  302         if ((borrow != parent)  && (borrow != NULL)) {
  303                 printf("cbq_add_class: borrow class != parent\n");
  304                 return (EINVAL);
  305         }
  306 
  307         /*
  308          * check parameters
  309          */
  310         switch (opts->flags & CBQCLF_CLASSMASK) {
  311         case CBQCLF_ROOTCLASS:
  312                 if (parent != NULL)
  313                         return (EINVAL);
  314                 if (cbqp->ifnp.root_)
  315                         return (EINVAL);
  316                 break;
  317         case CBQCLF_DEFCLASS:
  318                 if (cbqp->ifnp.default_)
  319                         return (EINVAL);
  320                 break;
  321         case 0:
  322                 if (a->qid == 0)
  323                         return (EINVAL);
  324                 break;
  325         default:
  326                 /* more than two flags bits set */
  327                 return (EINVAL);
  328         }
  329 
  330         /*
  331          * create a class.  if this is a root class, initialize the
  332          * interface.
  333          */
  334         if ((opts->flags & CBQCLF_CLASSMASK) == CBQCLF_ROOTCLASS) {
  335                 rmc_init(cbqp->ifnp.ifq_, &cbqp->ifnp, opts->ns_per_byte,
  336                     cbqrestart, a->qlimit, RM_MAXQUEUED,
  337                     opts->maxidle, opts->minidle, opts->offtime,
  338                     opts->flags);
  339                 cl = cbqp->ifnp.root_;
  340         } else {
  341                 cl = rmc_newclass(a->priority,
  342                                   &cbqp->ifnp, opts->ns_per_byte,
  343                                   rmc_delay_action, a->qlimit, parent, borrow,
  344                                   opts->maxidle, opts->minidle, opts->offtime,
  345                                   opts->pktsize, opts->flags);
  346         }
  347         if (cl == NULL)
  348                 return (ENOMEM);
  349 
  350         /* return handle to user space. */
  351         cl->stats_.handle = a->qid;
  352         cl->stats_.depth = cl->depth_;
  353 
  354         /* save the allocated class */
  355         cbqp->cbq_class_tbl[i] = cl;
  356 
  357         if ((opts->flags & CBQCLF_CLASSMASK) == CBQCLF_DEFCLASS)
  358                 cbqp->ifnp.default_ = cl;
  359 
  360         return (0);
  361 }
  362 
  363 int
  364 cbq_remove_queue(struct pf_altq *a)
  365 {
  366         struct rm_class *cl;
  367         cbq_state_t     *cbqp;
  368         int             i;
  369 
  370         if ((cbqp = a->altq_disc) == NULL)
  371                 return (EINVAL);
  372 
  373         if ((cl = clh_to_clp(cbqp, a->qid)) == NULL)
  374                 return (EINVAL);
  375 
  376         /* if we are a parent class, then return an error. */
  377         if (is_a_parent_class(cl))
  378                 return (EINVAL);
  379 
  380         /* delete the class */
  381         rmc_delete_class(&cbqp->ifnp, cl);
  382 
  383         /*
  384          * free the class handle
  385          */
  386         for (i = 0; i < CBQ_MAX_CLASSES; i++)
  387                 if (cbqp->cbq_class_tbl[i] == cl) {
  388                         cbqp->cbq_class_tbl[i] = NULL;
  389                         if (cl == cbqp->ifnp.root_)
  390                                 cbqp->ifnp.root_ = NULL;
  391                         if (cl == cbqp->ifnp.default_)
  392                                 cbqp->ifnp.default_ = NULL;
  393                         break;
  394                 }
  395 
  396         return (0);
  397 }
  398 
  399 int
  400 cbq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes)
  401 {
  402         cbq_state_t     *cbqp;
  403         struct rm_class *cl;
  404         class_stats_t    stats;
  405         int              error = 0;
  406 
  407         if ((cbqp = altq_lookup(a->ifname, ALTQT_CBQ)) == NULL)
  408                 return (EBADF);
  409 
  410         if ((cl = clh_to_clp(cbqp, a->qid)) == NULL)
  411                 return (EINVAL);
  412 
  413         if (*nbytes < sizeof(stats))
  414                 return (EINVAL);
  415 
  416         get_class_stats(&stats, cl);
  417 
  418         if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0)
  419                 return (error);
  420         *nbytes = sizeof(stats);
  421         return (0);
  422 }
  423 
  424 /*
  425  * int
  426  * cbq_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pattr)
  427  *              - Queue data packets.
  428  *
  429  *      cbq_enqueue is set to ifp->if_altqenqueue and called by an upper
  430  *      layer (e.g. ether_output).  cbq_enqueue queues the given packet
  431  *      to the cbq, then invokes the driver's start routine.
  432  *
  433  *      Assumptions:    called in splnet
  434  *      Returns:        0 if the queueing is successful.
  435  *                      ENOBUFS if a packet dropping occurred as a result of
  436  *                      the queueing.
  437  */
  438 
  439 static int
  440 cbq_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pktattr)
  441 {
  442         cbq_state_t     *cbqp = (cbq_state_t *)ifq->altq_disc;
  443         struct rm_class *cl;
  444         int              len;
  445 
  446         /* grab class set by classifier */
  447         if ((m->m_flags & M_PKTHDR) == 0) {
  448                 /* should not happen */
  449                 printf("altq: packet for %s does not have pkthdr\n",
  450                     ifq->altq_ifp->if_xname);
  451                 m_freem(m);
  452                 return (ENOBUFS);
  453         }
  454         if ((cl = clh_to_clp(cbqp, m->m_pkthdr.pf.qid)) == NULL) {
  455                 cl = cbqp->ifnp.default_;
  456                 if (cl == NULL) {
  457                         m_freem(m);
  458                         return (ENOBUFS);
  459                 }
  460                 cl->pktattr_ = NULL;
  461         }
  462 
  463         len = m_pktlen(m);
  464         if (rmc_queue_packet(cl, m) != 0) {
  465                 /* drop occurred.  some mbuf was freed in rmc_queue_packet. */
  466                 PKTCNTR_ADD(&cl->stats_.drop_cnt, len);
  467                 return (ENOBUFS);
  468         }
  469 
  470         /* successfully queued. */
  471         ++cbqp->cbq_qlen;
  472         IFQ_INC_LEN(ifq);
  473         return (0);
  474 }
  475 
  476 static struct mbuf *
  477 cbq_dequeue(struct ifaltq *ifq, int op)
  478 {
  479         cbq_state_t     *cbqp = (cbq_state_t *)ifq->altq_disc;
  480         struct mbuf     *m;
  481 
  482         m = rmc_dequeue_next(&cbqp->ifnp, op);
  483 
  484         if (m && op == ALTDQ_REMOVE) {
  485                 --cbqp->cbq_qlen;  /* decrement # of packets in cbq */
  486                 IFQ_DEC_LEN(ifq);
  487 
  488                 /* Update the class. */
  489                 rmc_update_class_util(&cbqp->ifnp);
  490         }
  491         return (m);
  492 }
  493 
  494 /*
  495  * void
  496  * cbqrestart(queue_t *) - Restart sending of data.
  497  * called from rmc_restart in splnet via timeout after waking up
  498  * a suspended class.
  499  *      Returns:        NONE
  500  */
  501 
  502 static void
  503 cbqrestart(struct ifaltq *ifq)
  504 {
  505         cbq_state_t     *cbqp;
  506         struct ifnet    *ifp;
  507 
  508         if (!ALTQ_IS_ENABLED(ifq))
  509                 /* cbq must have been detached */
  510                 return;
  511 
  512         if ((cbqp = (cbq_state_t *)ifq->altq_disc) == NULL)
  513                 /* should not happen */
  514                 return;
  515 
  516         ifp = ifq->altq_ifp;
  517         if (ifp->if_start &&
  518             cbqp->cbq_qlen > 0 && (ifp->if_flags & IFF_OACTIVE) == 0)
  519                 (*ifp->if_start)(ifp);
  520 }
  521 
  522 static void cbq_purge(cbq_state_t *cbqp)
  523 {
  524         struct rm_class *cl;
  525         int              i;
  526 
  527         for (i = 0; i < CBQ_MAX_CLASSES; i++)
  528                 if ((cl = cbqp->cbq_class_tbl[i]) != NULL)
  529                         rmc_dropall(cl);
  530         if (ALTQ_IS_ENABLED(cbqp->ifnp.ifq_))
  531                 cbqp->ifnp.ifq_->ifq_len = 0;
  532 }

/* [<][>][^][v][top][bottom][index][help] */