home *** CD-ROM | disk | FTP | other *** search
/ ftp.ee.lbl.gov / 2014.05.ftp.ee.lbl.gov.tar / ftp.ee.lbl.gov / cbq-1.1.tar.Z / cbq-1.1.tar / rm_class.c < prev    next >
C/C++ Source or Header  |  1995-11-03  |  19KB  |  581 lines

  1. /*
  2.  * Copyright (c) 1995 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *     This product includes software developed by the Network Research
  16.  *     Group at Lawrence Berkeley National Laboratory.
  17.  * 4. Neither the name of the University nor of the Laboratory may be used
  18.  *    to endorse or promote products derived from this software without
  19.  *    specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. /*
  35.  * There are 5 externally visible routines associated with resource
  36.  * management:
  37.  *
  38.  * rmc_init()    is called once at system startup by the interface
  39.  *        'attach' routine.  It initializes all the resource
  40.  *        management data structures associated with a particular
  41.  *        network interface.
  42.  *
  43.  * rmc_queue_packet() is called by the interface output routine
  44.  *        (ifp->if_output) to queue a packet on some resource
  45.  *        class and, if the interface isn't busy, start output.
  46.  *
  47.  * rmc_dequeue_next() is called by the routine that actually starts
  48.  *        output on an interface (e.g., lestart) to get the
  49.  *        next packet to be output.
  50.  *
  51.  * rmc_update_util() is called by the interface transmit done interrupt
  52.  *        service code to update the class bandwidth utilization 
  53.  *        accounting for the packet that just completed.
  54.  *
  55.  * rmc_newclass() is called by a setsockopt or protocol specific routine
  56.  *        to create a new resource class.
  57.  *
  58.  * The remaining routines in this file are 'internal' routines used
  59.  * by the above 5.
  60.  *
  61.  * Note that this is a research prototype.  It was designed to be easy
  62.  * to graft onto an existing system running BSD networking code and to
  63.  * be easy to debug.  This code is not particularly efficient -- a
  64.  * `production' version could easily be sped up by a factor of somewhere
  65.  * between 2 and 10.  But we still have a lot of research to do and our
  66.  * plan is to make it right before we make it fast.  - vj
  67.  */
  68.  
  69. #ifndef lint
  70. static char rcsid[] =
  71.     "@(#) $Header: rm_class.c,v 1.3 95/08/09 18:58:10 van Locked $ (LBL)";
  72. #endif
  73. /* Plus changes by Sally in the comments for setting maxidle, 10/95. */
  74.  
  75. #include <sys/param.h>
  76. #include <sys/mbuf.h>
  77. #include <sys/buf.h>
  78. #include <sys/map.h>
  79. #include <sys/socket.h>
  80. #include <sys/systm.h>
  81. #include <sys/errno.h>
  82. #include <sys/debug.h>
  83.  
  84. #include <sys/time.h>
  85. #include <sys/kernel.h>
  86.  
  87. #include <net/if.h>
  88. #include <net/if_arp.h>
  89. #include <net/netisr.h>
  90. #include <netinet/in.h>
  91. #include <netinet/in_systm.h>
  92. #include <netinet/ip.h>
  93. #include "rm_class.h"
  94.  
  95.  
  96. /*
  97.  * Macros for dealing with time values.  We assume all times are
  98.  * 'timevals'.  `microtime' is used to get the best available clock
  99.  * resolution.  If `microtime' *doesn't* return a value that's about
  100.  * ten times smaller than the average packet time on the fastest
  101.  * link that will use these routines, a slightly different clock
  102.  * scheme than this one should be used.
  103.  * (Bias due to truncation error in this scheme will overestimate utilization
  104.  * and discriminate against high bandwidth classes.  To remove this bias an
  105.  * integrator needs to be added.  The simplest integrator uses a history of
  106.  * 10 * avg.packet.time / min.tick.time packet completion entries.  This is
  107.  * straight forward to add but we don't want to pay the extra memory
  108.  * traffic to maintain it if it's not necessary (occasionally a vendor
  109.  * accidentally builds a workstation with a decent clock - e.g., Sun & HP).) 
  110.  */
  111.  
  112. #define RM_GETTIME(now) microtime(&now)
  113.  
  114. #define TV_LT(a, b) (((a).tv_usec < (b).tv_usec && \
  115.               (a).tv_sec <= (b).tv_sec) || (a).tv_sec < (b).tv_sec)
  116.  
  117. #define TV_DELTA(a, b, delta) { \
  118.         register int xxs; \
  119.  \
  120.         delta = (a).tv_usec - (b).tv_usec; \
  121.         if ((xxs = (a).tv_sec - (b).tv_sec)) { \
  122.             switch (xxs) { \
  123.             default: \
  124.                 if (xxs < 0) \
  125.                     panic("rm_class: bogus time values"); \
  126.                 delta = 0; \
  127.                 /* fall through */ \
  128.             case 2: \
  129.                 delta += 1000000; \
  130.                 /* fall through */ \
  131.             case 1: \
  132.                 delta += 1000000; \
  133.                 break; \
  134.             } \
  135.         } \
  136. }
  137.  
  138. #define TV_ADD_DELTA(a, delta, res) { \
  139.         register int xxus = (a).tv_usec + (delta); \
  140.  \
  141.         res.tv_sec = a.tv_sec; \
  142.         while (xxus >= 1000000) { \
  143.             ++((res).tv_sec); \
  144.             xxus -= 1000000; \
  145.         } \
  146.         (res).tv_usec = xxus; \
  147. }
  148.  
  149. /*
  150.  * Table for mapping a bit mask (with one bit per priority level)
  151.  * into the number of the highest priority set bit.
  152.  */
  153. u_char rmc_mask2pri[] = {
  154.     0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, /* 00 - 0f */
  155.     4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, /* 10 - 1f */
  156.     5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, /* 20 - 2f */
  157.     5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, /* 30 - 3f */
  158.     6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, /* 40 - 4f */
  159.     6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, /* 50 - 5f */
  160.     6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, /* 60 - 6f */
  161.     6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, /* 70 - 7f */
  162.     7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, /* 80 - 8f */
  163.     7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, /* 90 - 9f */
  164.     7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, /* a0 - af */
  165.     7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, /* b0 - bf */
  166.     7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, /* c0 - cf */
  167.     7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, /* d0 - df */
  168.     7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, /* e0 - ef */
  169.     7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7  /* f0 - ff */
  170. };
  171.  
  172.  
  173. /*
  174.  * Create a new resource management class at priority 'pri' on the
  175.  * interface given by 'ifdat'.
  176.  *
  177.  * nsecPerByte    is the data rate of the interface in nanoseconds/byte.
  178.  *        E.g., 800 for a 10Mb/s ethernet.  If the class gets less
  179.  *        than 100% of the bandwidth, this number should be the
  180.  *        'effective' rate for the class.  Let f be the
  181.  *        bandwidth fraction allocated to this class, and let
  182.  *        nsPerByte be the data rate of the output link in
  183.  *        nanoseconds/byte.  Then nsecPerByte is set to
  184.  *        nsPerByte / f.  E.g., 1600 (= 800 / .5)
  185.  *        for a class that gets 50% of an ethernet's bandwidth.
  186.  *
  187.  * action    the routine to call when the class is over limit.
  188.  *
  189.  * maxq        max allowable queue size for class (in packets).
  190.  *
  191.  * parent    parent class pointer.
  192.  *
  193.  * borrow    class to borrow from (should be either 'parent' or null).
  194.  *
  195.  * maxidle    max value allowed for class 'idle' time estimate (this
  196.  *        parameter determines how large an initial burst of packets
  197.  *        can be before overlimit action is invoked.
  198.  *
  199.  * offtime    how long 'delay' action will delay when class goes over
  200.  *        limit (this parameter determines the steady-state burst
  201.  *        size when a class is running over its limit).
  202.  *
  203.  * Maxidle and offtime have to be computed from the following:  If the
  204.  * average packet size is s, the bandwidth fraction allocated to this
  205.  * class is f, we want to allow b packet bursts, and the gain of the 
  206.  * averaging filter is g (= 1 - 2^(-RM_FILTER_GAIN)), then:
  207.  *
  208.  *   ptime = s * nsPerByte * (1 - f) / f
  209.  *   maxidle = ptime * (1 - g^b) / g^b
  210.  *   offtime = ptime * (1 + 1/(1 - g) * (1 - g^(b - 1)) / g^(b - 1)
  211.  *
  212.  * Operationally, it's convenient to specify maxidle & offtime in units
  213.  * independent of the link bandwidth so the maxidle & offtime passed to
  214.  * this routine are the above values multiplied by 8*f/(1000*nsPerByte).  
  215.  * (The constant factor is a scale factor needed to make the parameters
  216.  * integers.  This scaling also means that the 'unscaled' values of
  217.  * maxidle*nsecPerByte/8 and offtime*nsecPerByte/8 will be in microseconds,
  218.  * not nanoseconds.)  Also note that the 'idle' filter computation keeps
  219.  * an estimate scaled upward by 2^RM_FILTER_GAIN so the passed value of 
  220.  * maxidle also must be scaled upward by this value.  Thus, the passed 
  221.  * values for maxidle and offtime can be computed as follows:
  222.  *
  223.  * maxidle = maxidle * 2^RM_FILTER_GAIN * 8 / (1000 * nsecPerByte)
  224.  * offtime = offtime * 8 / (1000 * nsecPerByte)
  225.  */
  226. struct rm_class *
  227. rmc_newclass(pri, ifdat, nsecPerByte, action, maxq,
  228.          parent, borrow, maxidle, offtime)
  229.     register int pri;
  230.     register struct rm_ifdat *ifdat;
  231.     register u_int nsecPerByte;
  232.     void (*action)();
  233.     register int maxq;
  234.     register struct rm_class *parent;
  235.     register struct rm_class *borrow;
  236.     register u_int maxidle;
  237.     register u_int offtime;
  238. {
  239.     register struct rm_class *cl;
  240.     register struct rm_class *peer;
  241.     register int i, tim;
  242.  
  243.     cl = (struct rm_class *)new_kmem_zalloc(sizeof(*cl), KMEM_SLEEP);
  244.     if (cl == NULL) {
  245.         panic("no space for resource management data structures.");
  246.     }
  247.     if (peer = ifdat->classes[pri]) {
  248.         /* find the last class at this pri */
  249.         cl->peer = peer;
  250.         while (peer->peer != ifdat->classes[pri])
  251.             peer = peer->peer;
  252.         peer->peer = cl;
  253.     } else {
  254.         ifdat->classes[pri] = cl;
  255.         ifdat->classlist[pri] = cl;
  256.         cl->peer = cl;
  257.     }
  258.     cl->parent = parent;
  259.     cl->borrow = borrow;
  260.     cl->ifdat = ifdat;
  261.     cl->priority = pri;
  262.     cl->qmax = maxq;
  263.     maxidle = (maxidle * nsecPerByte) >> 3;
  264.     if (maxidle == 0)
  265.         maxidle = 1;
  266.     cl->maxidle = maxidle;
  267.     cl->avgidle = maxidle;
  268.     offtime = (offtime * nsecPerByte) >> 3;
  269.     if (offtime == 0)
  270.         offtime = 1;
  271.     cl->offtime = offtime;
  272.     cl->overlimit = action;
  273.  
  274.     tim = 0;
  275.     for (i = 0; i < sizeof(cl->len2time)/sizeof(cl->len2time[0]); ++i) {
  276.         register int v = tim / 1000;
  277.  
  278.         cl->len2time[i] =  v == 0? 1 : v;
  279.         tim += nsecPerByte;
  280.     }
  281.     return (cl);
  282. }
  283.  
  284. void rmc_root_overlimit();
  285.  
  286. /*
  287.  * Initialize the resource management data structures associated
  288.  * with the output portion of interface 'ifp'.  'ifdat' is where
  289.  * the structures will be built (for backwards compatibility, the
  290.  * structures aren't kept in the ifnet struct).  'nsecPerByte' 
  291.  * gives the link speed (inverse of bandwidth) in nanoseconds/byte.
  292.  * 'restart' is the driver-specific routine that the generic 'delay
  293.  * until under limit' action will call to restart output.  `maxq'
  294.  * is the queue size of the 'link' & 'default' classes.  'maxqueued'
  295.  * is the maximum number of packets that the resource management
  296.  * code will allow to be queued 'downstream' (this is typically 1
  297.  * or 2 -- just enough to keep the interface busy during the packet
  298.  * completion interrupt latency).
  299.  */
  300. void
  301. rmc_init(ifp, ifdat, nsecPerByte, restart, maxq, maxqueued)
  302.     register struct ifnet *ifp;
  303.     register struct rm_ifdat *ifdat;
  304.     register u_int nsecPerByte;
  305.     void (*restart)();
  306.     int maxq, maxqueued;
  307. {
  308.     register struct rm_class *cl;
  309.  
  310.     bzero(ifdat, sizeof(*ifdat));
  311.     ifdat->ifp = ifp;
  312.     ifdat->restart = restart;
  313.     ifdat->maxqueued = maxqueued;
  314.  
  315.     /*
  316.      * allocate space for the root class with ~96% avail to borrow
  317.      * and 128 packet bursts.
  318.      */
  319.     cl = rmc_newclass(0, ifdat, (nsecPerByte * 100) / 96,
  320.               rmc_root_overlimit, maxq, 0, 0, 879, 61);
  321.  
  322.     /* default - low priority with 95% allocation */
  323.     ifdat->defaultclass = rmc_newclass(1, ifdat, (nsecPerByte * 100) / 95,
  324.                        rmc_delay_action, maxq, cl, cl,
  325.                        6, 5);
  326. }
  327.  
  328. /*
  329.  * Add packet given by mbuf 'm' to queue for resource class 'cl'.
  330.  * This routine is called by a driver's if_output routine.
  331.  * If the limit on packets queued to the interface hardware hasn't
  332.  * been reached, we call the interface 'restart' routine to try
  333.  * to output another packet.
  334.  *
  335.  * This routine must be called with output packet completion
  336.  * interrupts locked out (to avoid racing with rmc_dequeue_next).
  337.  */
  338. void
  339. rmc_queue_packet(cl, m)
  340.     register struct rm_class *cl;
  341.     register struct mbuf* m;
  342. {
  343.     register struct mbuf* m0;
  344.     register struct rm_ifdat *ifd = cl->ifdat;
  345.  
  346.     if ((m0 = cl->tail) != NULL)
  347.         m->m_act = m0->m_act;
  348.     else {
  349.         register int cpri = cl->priority;
  350.  
  351.         m0 = m;
  352.         if (++ifd->activecnt[cpri] == 1)
  353.             ifd->csum |= (1 << cpri);
  354.     }
  355.     m0->m_act = m;
  356.     cl->tail = m;
  357.     if (++cl->qcnt >= cl->qmax)
  358.         rmc_drop_action(cl);
  359.     else if (ifd->queued < ifd->maxqueued)
  360.         (ifd->restart)(ifd->ifp);
  361. }
  362.  
  363. /*
  364.  * Return 1 if class 'cl' is under limit or can borrow from a parent,
  365.  * 0 if overlimit.  As a side-effect, this routine will invoke the
  366.  * class overlimit action if the class if overlimit.
  367.  */
  368. int
  369. rmc_under_limit(cl)
  370.     register struct rm_class *cl;
  371. {
  372.     register struct rm_class *lcl = cl;
  373.     struct timeval now;
  374.  
  375.     if (cl->parent == 0)
  376.         /* root class is always under limit */
  377.         return (1);
  378.  
  379.     RM_GETTIME(now);
  380.     if (cl->sleeping) {
  381.         if (TV_LT(now, cl->undertime))
  382.             return (0);
  383.  
  384.         cl->sleeping = 0;
  385.         return (1);
  386.     }
  387.     while (cl->undertime.tv_sec && TV_LT(now, cl->undertime)) {
  388.         ++cl->borrows;
  389.         if ((cl = cl->borrow) == 0) {
  390.             ++lcl->overactions;
  391.             (lcl->overlimit)(lcl);
  392.             return (0);
  393.         }
  394.     }
  395.     return (1);
  396. }
  397.  
  398. /*
  399.  * Dequeue & return next packet from the highest priority class that
  400.  * has a packet to send & has enough allocation to send it.  This
  401.  * routine is called by a driver whenever it needs a new packet to
  402.  * output, typically in the XXstart routine (e.g., lestart, bfstart,
  403.  * etc.).  0 is returned if there is no packet to send.  As a side
  404.  * effect of calling this routine, `overlimit' actions are called
  405.  * for classes that have packets to send but are over their bandwidth
  406.  * limit & can't borrow from their parent.
  407.  */
  408. struct mbuf *
  409. rmc_dequeue_next(ifd)
  410.     register struct rm_ifdat *ifd;
  411. {
  412.     register struct mbuf *m, *m0;
  413.     register int csum, cpri;
  414.     register struct rm_class *cl;
  415.  
  416.     csum = ifd->csum;
  417.     while (csum) {
  418.         register struct rm_class *clh;
  419.  
  420.         cpri = rmc_mask2pri[csum];
  421.         clh = cl = ifd->classlist[cpri];
  422.         do {
  423.             if ((m = cl->tail) &&
  424.                 (cl->undertime.tv_sec == 0 || rmc_under_limit(cl)))
  425.                 goto out;
  426.             cl = cl->peer;
  427.         } while (cl != clh);
  428.  
  429.         csum &=~ (1 << cpri);
  430.     }
  431.     return (0);
  432.   out:
  433.     ifd->classlist[cpri] = cl->peer;
  434.     ifd->class = cl;
  435.  
  436.     if ((m0 = m->m_act) != m)
  437.         m->m_act = m0->m_act;
  438.     else {
  439.         cl->tail = 0;
  440.         if (--ifd->activecnt[cpri] <= 0)
  441.             ifd->csum &=~ (1 << cpri);
  442.     }
  443.     --cl->qcnt;
  444.     ++ifd->queued;
  445.     return (m0);
  446. }
  447.  
  448. /*
  449.  * Update the utilization estimate for the packet that just completed.
  450.  * The packet's class & the parent(s) of that class all get their
  451.  * estimators updated.  This routine is called by the driver's output-
  452.  * packet-completion interrupt service routine.
  453.  */
  454. void
  455. rmc_update_util(ifd)
  456.     register struct rm_ifdat *ifd;
  457. {
  458.     register struct rm_class *cl = ifd->class;
  459.     register int pktlen = ifd->curlen;
  460.         register int idle, avgidle;
  461.     struct timeval now;
  462.  
  463.     --ifd->queued;
  464.     ++cl->npackets;
  465.     cl->nbytes += pktlen;
  466.     RM_GETTIME(now);
  467.     do {
  468.         TV_DELTA(now, cl->last, idle);
  469.         idle -= cl->len2time[pktlen];
  470.         avgidle = cl->avgidle;
  471.         avgidle += idle - (avgidle >> RM_FILTER_GAIN);
  472.  
  473.         if (avgidle <= 0) {
  474.             cl->avgidle = 0;
  475.             cl->undertime = now;
  476.             TV_ADD_DELTA(now, cl->offtime, cl->undertime);
  477.             ++cl->over;
  478.         } else {
  479.             cl->avgidle = (avgidle > cl->maxidle)?  cl->maxidle :
  480.                                 avgidle;
  481.             cl->undertime.tv_sec = 0;
  482.         }
  483.         cl->last = now;
  484.     } while (cl = cl->parent);
  485. }
  486.  
  487. /*
  488.  * Generic (not protocol-specific) over-limit action routines.  These
  489.  * get invoked by rmc_under_limit() if a class with packets to send
  490.  * is over its bandwidth limit & can't borrow from a parent class.
  491.  */
  492. void
  493. rmc_drop_action(cl)
  494.     register struct rm_class *cl;
  495. {
  496.     register struct mbuf *m, *m0;
  497.  
  498.     if ((m = cl->tail) == NULL)
  499.         panic("rmc_drop_action: empty queue");
  500.     if ((m0 = m->m_act) != m)
  501.         m->m_act = m0->m_act;
  502.     else {
  503.         register struct rm_ifdat *ifd = cl->ifdat;
  504.         register int cpri = cl->priority;
  505.  
  506.         cl->tail = 0;
  507.         if (--ifd->activecnt[cpri] <= 0)
  508.             ifd->csum &=~ (1 << cpri);
  509.     }
  510.     --cl->qcnt;
  511.     ++cl->drops;
  512.     m_freem(m0);
  513. }
  514.  
  515. /*
  516.  * rmc_delay_action() implements the 'delay' (i.e., rate-limiting) action
  517.  * for a class that goes over it's bandwidth limit.  It simply schedules
  518.  * a timeout to restart sending for the class at the future time when
  519.  * the class would go under its limit.
  520.  */
  521. void
  522. rmc_delay_action(cl)
  523.     register struct rm_class *cl;
  524. {
  525.     register int t = hzto(cl->undertime);
  526.  
  527.     /*
  528.      * since packets are phased randomly with respect to the
  529.      * clock, 1 tick (the next clock tick) can be an arbitrarily
  530.      * short time so we have to wait for at least two ticks.
  531.      * Note that we always start a timer even though in the usual
  532.      * case packet traffic will cause this class be be rescanned
  533.      * and restarted rather than the relatively coarse system
  534.      * timer:  If there's no other traffic, we need the timer as
  535.      * a 'backstop' to restart this class.
  536.      */
  537.     if (t <= 1)
  538.         t = 2;
  539.     cl->sleeping = 1;
  540.     timeout(rmc_restart, (caddr_t)cl, t);
  541. }
  542.  
  543. /*
  544.  * rmc_restart() is just a helper routine for rmc_delay_action -- it is
  545.  * called by the system timer code & is responsible checking if the
  546.  * class is still sleeping (it might have been restarted as a side
  547.  * effect of the queue scan on a packet arrival) and, if so, restarting
  548.  * output for the class.  Inspecting the class state & restarting output
  549.  * require locking the class structure.  In general the driver is
  550.  * responsible for locking but this is the only routine that is not
  551.  * called directly or indirectly from the interface driver so it has
  552.  * know about system locking conventions.  Under bsd, locking is done
  553.  * by raising IPL to splimp so that's what's implemented here.  On a
  554.  * different system this would probably need to be changed.
  555. void
  556. rmc_restart(cl)
  557.     register struct rm_class *cl;
  558. {
  559.     register int s = splimp();
  560.  
  561.     if (cl->sleeping) {
  562.         register struct rm_ifdat *ifd = cl->ifdat;
  563.  
  564.         cl->sleeping = 0;
  565.         if (ifd->queued < ifd->maxqueued)
  566.             (ifd->restart)(ifd->ifp);
  567.     }
  568.     splx(s);
  569. }
  570.  
  571. /*
  572.  * We should never get here (it should not be possible for the root
  573.  * class to go overlimit) so panic & get a crash dump if we do.
  574.  */
  575. void
  576. rmc_root_overlimit(cl)
  577.     register struct rm_class *cl;
  578. {
  579.     panic("rmc_root_overlimit");
  580. }
  581.