cbq.cc

Go to the documentation of this file.
00001 /* -*-  Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
00002 /*
00003  * Copyright (c) 1997 The Regents of the University of California.
00004  * All rights reserved.
00005  * 
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice, this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright
00012  *    notice, this list of conditions and the following disclaimer in the
00013  *    documentation and/or other materials provided with the distribution.
00014  * 3. All advertising materials mentioning features or use of this software
00015  *    must display the following acknowledgement:
00016  *  This product includes software developed by the Network Research
00017  *  Group at Lawrence Berkeley National Laboratory.
00018  * 4. Neither the name of the University nor of the Laboratory may be used
00019  *    to endorse or promote products derived from this software without
00020  *    specific prior written permission.
00021  * 
00022  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00023  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00024  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00025  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00026  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00027  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00028  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00029  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00031  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00032  * SUCH DAMAGE.
00033  */
00034 
00035 #ifndef lint
00036 static const char rcsid[] =
00037     "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/queue/cbq.cc,v 1.28 2005/07/27 01:13:44 tomh Exp $ (LBL)";
00038 #endif
00039 
00040 
00041 //
00042 // new version of cbq using the ns-2 fine-grain
00043 // objects.  Also, re-orginaize CBQ to look more like how
00044 // its description reads in ToN v3n4 and simplify extraneous stuff -KF
00045 //
00046 // there is a 1-1 relationship between classes and queues, except
00047 // that internal nodes in the LS tree don't have queues
00048 //
00049 // Definitions:
00050 //  overlimit:
00051 //      recently used more than allocated link-sharing bandwidth
00052 //          (in bytes/sec averaged over specified interval)
00053 //  
00054 //  level:
00055 //      all leaves are at level 1
00056 //      interior nodes are at a level 1 greater than
00057 //          the highest level number of any of its children
00058 //
00059 //  unsatisfied:
00060 //      (leaf): underlimit and has demand
00061 //      (interior): underlimit and has some descendant w/demand
00062 //          [either a leaf or interior descendant]
00063 //
00064 //  formal link-sharing:
00065 //      class may continue unregulated if either:
00066 //      1> class is underlimit or at-limit
00067 //      2> class has a under(at)-limit ancestor at level i
00068 //          and no unsatisfied classes at any levels < i
00069 //
00070 //  ancestors-only link-sharing:
00071 //      class may continue unregulated if either:
00072 //      1> class is under/at limit
00073 //      2> class has an UNDER-limit ancestor [at-limit not ok]
00074 //
00075 //  top-level link-sharing:
00076 //      class may continue unregulated if either:
00077 //      1> class is under/at limit
00078 //      2> class has an UNDER-limit ancestor with level
00079 //          <= the value of "top-level"
00080 
00081 #include "queue-monitor.h"
00082 #include "queue.h"
00083 #include "delay.h"
00084 
00085 #define MAXPRIO     10  /* # priorities in scheduler */
00086 #define MAXLEVEL    32  /* max depth of link-share tree(s) */
00087 #define LEAF_LEVEL  1   /* level# for leaves */
00088 #define POWEROFTWO  16
00089 
00090 class CBQueue;
00091 
00092 class CBQClass : public Connector {
00093 public:
00094     friend class CBQueue;
00095     friend class WRR_CBQueue;
00096 
00097     CBQClass();
00098     int command(int argc, const char*const* argv);
00099     void    recv(Packet*, Handler*);    // from upstream classifier
00100 
00101 protected:
00102 
00103     void    newallot(double);       // change an allotment
00104     void    update(Packet*, double);    // update when sending pkt
00105     void    delayed(double);        // when overlim/can't borrow
00106 
00107     int satisfied(double);      // satisfied?
00108     int     demand();           // do I have demand?
00109     int     leaf();             // am I a leaf class?
00110 
00111     int ancestor(CBQClass*p);       // are we an ancestor of p?
00112     int desc_with_demand();     // any desc has demand?
00113 
00114     CBQueue*    cbq_;           // the CBQueue I'm part of
00115 
00116     CBQClass*   peer_;          // peer at same sched prio level
00117     CBQClass*   level_peer_;        // peer at same LS level
00118     CBQClass*   lender_;        // parent I can borrow from
00119 
00120     Queue*      q_;         // underlying queue
00121     QueueMonitor*   qmon_;          // monitor for the queue
00122 
00123     double      allotment_;     // frac of link bw
00124     double      maxidle_;       // bound on idle time
00125     double      maxrate_;       // bound on bytes/sec rate
00126     double      extradelay_;        // adjustment to delay
00127     double      last_time_;     // last xmit time this class
00128     double      undertime_;     // will become unsat/eligible
00129     double      avgidle_;       // EWMA of idle
00130     int     pri_;           // priority for scheduler
00131     int     level_;         // depth in link-sharing tree
00132     int     delayed_;       // boolean-was I delayed
00133     int     bytes_alloc_;       // for wrr only
00134     int     permit_borrowing_;  // ok to borrow?
00135 
00136 };
00137 
00138 class CBQueue : public Queue {
00139 public:
00140     CBQueue();
00141     void        reset();
00142     void        enque(Packet*) { abort(); }
00143     void        recv(Packet*, Handler*);
00144     LinkDelay*  link() const { return (link_); }
00145     CBQClass*   level(int n) const { return levels_[n]; }
00146     Packet*     deque();
00147     virtual int command(int argc, const char*const* argv);
00148     virtual void    addallot(int, double) { }
00149     Packet* pending_pkt() const { return (pending_pkt_); }
00150     void        sched();
00151     int     toplevel() {    // are we using toplevel?
00152 //      return (eligible_ == &eligible_toplevel);
00153         return (eligible_ == TOPLEVEL);
00154     }
00155     void        toplevel_arrival(CBQClass*, double);
00156 protected:
00157     Event       intr_;
00158     int     algorithm(const char *);
00159     virtual int insert_class(CBQClass*);
00160     int     send_permitted(CBQClass*, double);
00161     CBQClass*   find_lender(CBQClass*, double);
00162     void        toplevel_departure(CBQClass*, double);
00163 
00164     CBQClass*   last_lender_;
00165     Packet*     pending_pkt_;       // queued packet
00166     LinkDelay*  link_;          // managed link
00167 
00168     CBQClass*   active_[MAXPRIO];   // classes at prio of index
00169     CBQClass*   levels_[MAXLEVEL+1];    // LL of classes per level
00170     int     maxprio_;       // highest prio# seen
00171     int     maxpkt_;        // largest pkt (used by WRR)
00172     int     maxlevel_;      // highest level# seen
00173     int     toplevel_;      // for top-level LS
00174 
00175 //  typedef int (CBQueue::*eligible_type_)(CBQClass*, double);
00176 //  eligible_type_ eligible_;       // eligible function
00177     enum eligible_type_ { NONE, FORMAL, ANCESTORS, TOPLEVEL };
00178     eligible_type_ eligible_;
00179     int eligible_formal(CBQClass*, double);
00180     int eligible_ancestors(CBQClass*, double) { return (1); }
00181     int eligible_toplevel(CBQClass* cl, double) {
00182         return(cl->level_ <= toplevel_);
00183     }
00184 };
00185 
00186 static class CBQQueueClass : public TclClass {
00187 public:
00188     CBQQueueClass() : TclClass("Queue/CBQ") { }
00189     TclObject* create(int, const char*const*) {
00190         return (new CBQueue);
00191     }
00192 } class_cbq;
00193 
00194 static class CBQClassClass : public TclClass {
00195 public:
00196     CBQClassClass() : TclClass("CBQClass") { }
00197     TclObject* create(int, const char*const*) {
00198         return (new CBQClass);
00199     }
00200 } class_cbqclass;
00201 
00202 CBQueue::CBQueue() : last_lender_(NULL), pending_pkt_(NULL), link_(NULL),
00203     maxprio_(-1), maxpkt_(-1), maxlevel_(-1), toplevel_(MAXLEVEL),
00204 //  eligible_((eligible_type_)NULL)
00205     eligible_(NONE)
00206 {
00207     bind("maxpkt_", &maxpkt_);
00208     memset(active_, '\0', sizeof(active_));
00209     memset(levels_, '\0', sizeof(levels_));
00210 }
00211 
00212 /*
00213  * schedule ourselves, used by CBQClass::recv
00214  */
00215 
00216 void
00217 CBQueue::sched()
00218 {
00219     Scheduler& s = Scheduler::instance();
00220     blocked_ = 1;
00221     s.schedule(&qh_, &intr_, 0);
00222 }
00223 
00224 /*
00225  * invoked by passing a packet from one of our managed queues
00226  * basically provides a queue of one packet
00227  */
00228 
00229 void
00230 CBQueue::recv(Packet* p, Handler*)
00231 {
00232 
00233     if (pending_pkt_ != NULL)
00234         abort();
00235 
00236     blocked_ = 1;
00237     pending_pkt_ = p;
00238 }
00239 
00240 void
00241 CBQueue::reset()
00242 {
00243     // don't do anything
00244     // in particular, don't let Queue::reset() call
00245     // our deque() method
00246 }
00247 
00248 int
00249 CBQueue::algorithm(const char *arg)
00250 {
00251 
00252     if (*arg == '0' || (strcmp(arg, "ancestor-only") == 0)) {
00253 //      eligible_ = &eligible_ancestors;
00254         eligible_ = ANCESTORS;
00255         return (1);
00256     } else if (*arg == '1' || (strcmp(arg, "top-level") == 0)) {
00257 //      eligible_ = &eligible_toplevel;
00258         eligible_ = TOPLEVEL;
00259         return (1);
00260     } else if (*arg == '2' || (strcmp(arg, "formal") == 0)) {
00261 //      eligible_ = &eligible_formal;
00262         eligible_ = FORMAL;
00263         return (1);
00264     } else if (*arg == '3' || (strcmp(arg, "old-formal") == 0)) {
00265         fprintf(stderr, "CBQ: old-formal LS not supported\n");
00266         return (-1);
00267     }
00268     return (-1);
00269 }
00270 
00271 /*
00272  *
00273  * toplevel_arrival:    called only using TL link sharing on arrival
00274  * toplevel_departure:  called only using TL link sharing on departure
00275  */
00276 
00277 void
00278 CBQueue::toplevel_departure(CBQClass *cl, double now)
00279 {
00280     if (toplevel_ >= last_lender_->level_) {
00281         if ((cl->qmon_->pkts() <= 1) ||
00282             last_lender_->undertime_ > now) {
00283             toplevel_ = MAXLEVEL;
00284         } else {
00285             toplevel_ = last_lender_->level_;
00286         }
00287     }
00288 }
00289 
00290 void
00291 CBQueue::toplevel_arrival(CBQClass *cl, double now)
00292 {
00293     if (toplevel_ > 1) {
00294         if (cl->undertime_ < now)
00295             toplevel_ = 1;
00296         else if (toplevel_ > 2 && cl->permit_borrowing_ && cl->lender_ != NULL) {
00297             if (cl->lender_->undertime_ < now)
00298                 toplevel_ = 2;
00299         }
00300     }
00301 }
00302 
00303 /*
00304  * deque: this gets invoked by way of our downstream
00305  * (i.e. linkdelay) neighbor doing a 'resume' on us
00306  * via our handler (by Queue::resume()), or by our upstream
00307  * neighbor when it gives us a packet when we were
00308  * idle
00309  */
00310 
00311 Packet *
00312 CBQueue::deque()
00313 {
00314 
00315     Scheduler& s = Scheduler::instance();
00316     double now = s.clock();
00317 
00318     CBQClass* first = NULL;
00319     CBQClass* eligible = NULL;
00320     CBQClass* cl;
00321     register int prio;
00322     Packet* rval;
00323 
00324     int none_found = 0;
00325 
00326     /*
00327      * prio runs from 0 .. maxprio_
00328      *
00329      * round-robin through all the classes at priority 'prio'
00330      *  if any class is ok to send, resume it's queue
00331      * go on to next lowest priority (higher prio nuber) and repeat
00332      * [lowest priority number is the highest priority]
00333      */
00334 
00335     for (prio = 0; prio <= maxprio_; prio++) {
00336         // see if there is any class at this prio
00337         if ((cl = active_[prio]) == NULL) {
00338             // nobody at this prio level
00339             continue;
00340         }
00341 
00342         // look for underlimit peer with something to send
00343         do {
00344             // anything to send?
00345             if (cl->demand()) {
00346                 if (first == NULL && cl->permit_borrowing_ && cl->lender_ != NULL)
00347                     first = cl;
00348                 if (send_permitted(cl, now)) {
00349                     // ok to send
00350                     eligible = cl;
00351                     goto found;
00352                 } else {
00353                     // not ok right now
00354                     cl->delayed(now);
00355                 }
00356             }
00357             cl = cl->peer_; // move to next at same prio
00358         } while (cl != active_[prio]);
00359     }
00360     // did not find anyone so let first go
00361     // eligible will be NULL at this point
00362     if (first != NULL) {
00363         none_found = 1;
00364         eligible = first;
00365     }
00366 
00367 found:
00368     if (eligible != NULL) {
00369         active_[eligible->pri_] = eligible->peer_;
00370         // eligible->q_->unblock();
00371         eligible->q_->resume(); // fills in pending
00372         if (pending_pkt_ && !none_found) {
00373             eligible->update(pending_pkt_, now);
00374             if (toplevel())
00375                 toplevel_departure(eligible, now);
00376         }
00377     }
00378     rval = pending_pkt_;
00379     pending_pkt_ = NULL;
00380 
00381     return (rval);
00382 }
00383 
00384 /*
00385  * we are permitted to send if either
00386  *  1> we are not overlimit (ie we are underlimit or at limit)
00387  *  2> one of the varios algorithm-dependent conditions is met
00388  *
00389  * if we are permitted, who did we borrow from? [could be ourselves
00390  * if we were not overlimit]
00391  */
00392 int CBQueue::send_permitted(CBQClass* cl, double now)
00393 {
00394     if (cl->undertime_ < now) {
00395         cl->delayed_ = 0;
00396         last_lender_ = cl;
00397         return (1);
00398     } else if (cl->permit_borrowing_ &&
00399            (((cl = find_lender(cl, now)) != NULL))) {
00400         last_lender_ = cl;
00401         return (1);
00402     }
00403     return (0);
00404 }
00405 
00406 /*
00407  * find_lender(class, time)
00408  *
00409  *  find a lender for the provided class according to the
00410  *  various algorithms
00411  *
00412  */
00413 
00414 CBQClass*
00415 CBQueue::find_lender(CBQClass* cl, double now)
00416 {
00417     if ((!cl->permit_borrowing_) || ((cl = cl->lender_) == NULL))
00418         return (NULL);      // no ancestor to borrow from
00419 
00420     while (cl != NULL) {
00421         // skip past overlimit ancestors
00422         //  if using TL and we're above the TL limit
00423         //  do early out
00424         if (cl->undertime_ > now) {
00425             if (toplevel() && cl->level_ > toplevel_)
00426                 return (NULL);
00427             cl = cl->lender_;
00428             continue;
00429         }
00430 
00431         // found what may be an eligible
00432         // lender, check using per-algorithm eligibility
00433         // criteria
00434         // XXX we explicitly invoke this indirect method with
00435         // the "this" pointer because MS VC++ can't parse it
00436         // without it...
00437 //      if ((this->*eligible_)(cl, now))
00438 //          return (cl);
00439         switch (eligible_) {
00440         case TOPLEVEL:
00441             if (eligible_toplevel(cl, now))
00442                 return (cl);
00443             break;
00444         case ANCESTORS:
00445             if (eligible_ancestors(cl, now))
00446                 return (cl);
00447             break;
00448         case FORMAL:
00449             if (eligible_formal(cl, now))
00450                 return (cl);
00451             break;
00452         default:
00453             fprintf(stderr, "Wrong eligible_\n");
00454             abort();
00455         }
00456         cl = cl->lender_;
00457     }
00458     return (cl);
00459 }
00460 
00461 /*
00462  * rule #2 for formal link-sharing
00463  *  class must have no unsatisfied classes below it
00464  */
00465 int
00466 CBQueue::eligible_formal(CBQClass *cl, double now)
00467 {
00468     int level;
00469     CBQClass *p;
00470 
00471     // check from leaf level to (cl->level - 1)
00472     for (level = LEAF_LEVEL; level < cl->level_; level++) {
00473         p = levels_[level];
00474         while (p != NULL) {
00475             if (!p->satisfied(now))
00476                 return (0);
00477             p = p->level_peer_;
00478         }
00479     }
00480     return (1);
00481 }
00482 
00483 /*
00484  * insert a class into the cbq object
00485  */
00486 
00487 int
00488 CBQueue::insert_class(CBQClass *p)
00489 {
00490     p->cbq_ = this;
00491 
00492     /*
00493      * Add to circularly-linked list "active_"
00494      *    of peers for the given priority.
00495      */
00496 
00497     if (p->pri_ < 0 || p->pri_ > (MAXPRIO-1)) {
00498         fprintf(stderr, "CBQ class %s has invalid pri %d\n",
00499             p->name(), p->pri_);
00500         return (-1);
00501     }
00502 
00503     if (p->q_ != NULL) {
00504         // only leaf nodes (which have associated queues)
00505         // are scheduled
00506         if (active_[p->pri_] != NULL) {
00507             p->peer_ = active_[p->pri_]->peer_;
00508             active_[p->pri_]->peer_ = p;
00509         } else {
00510             p->peer_ = p;
00511             active_[p->pri_] = p;
00512         }
00513         if (p->pri_ > maxprio_)
00514             maxprio_ = p->pri_;
00515     }
00516 
00517     /*
00518      * Compute maxrate from allotment.
00519      * convert to bytes/sec
00520      *  and store the highest prio# we've seen
00521      */
00522 
00523     if (p->allotment_ < 0.0 || p->allotment_ > 1.0) {
00524         fprintf(stderr, "CBQ class %s has invalid allot %f\n",
00525             p->name(), p->allotment_);
00526         return (-1);
00527     }
00528 
00529     if (link_ == NULL) {
00530         fprintf(stderr, "CBQ obj %s has no link!\n", name());
00531         return (-1);
00532     }
00533     if (link_->bandwidth() <= 0.0) {
00534         fprintf(stderr, "CBQ obj %s has invalid link bw %f on link %s\n",
00535             name(), link_->bandwidth(), link_->name());
00536         return (-1);
00537     }
00538 
00539     p->maxrate_ = p->allotment_ * (link_->bandwidth() / 8.0);
00540     addallot(p->pri_, p->allotment_);
00541 
00542     /*
00543      * Add to per-level list
00544      *  and store the highest level# we've seen
00545      */
00546 
00547     if (p->level_ <= 0 || p->level_ > MAXLEVEL) {
00548         fprintf(stderr, "CBQ class %s has invalid level %d\n",
00549             p->name(), p->level_);
00550         return (-1);
00551     }
00552 
00553     p->level_peer_ = levels_[p->level_];
00554     levels_[p->level_] = p;
00555     if (p->level_ > maxlevel_)
00556         maxlevel_ = p->level_;
00557 
00558     /*
00559      * Check that parent and borrow linkage are acyclic.
00560      */
00561 #ifdef notdef
00562     check_for_cycles(CBQClass::parent);
00563     check_for_cycles(CBQClass::borrow);
00564 #endif
00565     return 0;
00566 }
00567 
00568 int CBQueue::command(int argc, const char*const* argv)
00569 {
00570 
00571     Tcl& tcl = Tcl::instance();
00572     if (argc == 3) {
00573         if (strcmp(argv[1], "insert-class") == 0) {
00574             CBQClass *cl = (CBQClass*)TclObject::lookup(argv[2]);
00575             if (cl == 0) {
00576                 tcl.resultf("CBQ: no class object %s",
00577                     argv[2]);
00578                 return (TCL_ERROR);
00579             }
00580             if (insert_class(cl) < 0) {
00581                 tcl.resultf("CBQ: trouble inserting class %s",
00582                     argv[2]);
00583                 return (TCL_ERROR);
00584             }
00585             return (TCL_OK);
00586         }
00587         if (strcmp(argv[1], "link") == 0) {
00588             LinkDelay* del = (LinkDelay*)TclObject::lookup(argv[2]);
00589             if (del == 0) {
00590                 tcl.resultf("CBQ: no LinkDelay object %s",
00591                     argv[2]);
00592                 return(TCL_ERROR);
00593             }
00594             link_ = del;
00595             return (TCL_OK);
00596         }
00597         if (strcmp(argv[1], "algorithm") == 0) {
00598             if (algorithm(argv[2]) < 0)
00599                 return (TCL_ERROR);
00600             return (TCL_OK);
00601         }
00602     }
00603     return (Queue::command(argc, argv));
00604 }
00605 
00606 class WRR_CBQueue : public CBQueue {
00607 public:
00608     WRR_CBQueue() {
00609         memset(M_, '\0', sizeof(M_));
00610         memset(alloc_, '\0', sizeof(alloc_));
00611         memset(cnt_, '\0', sizeof(cnt_));
00612     }
00613     void    addallot(int prio, double diff) {
00614         alloc_[prio] += diff;
00615         setM();
00616     }
00617 protected:
00618     Packet *deque();
00619     int insert_class(CBQClass*);
00620     void    setM();
00621     double  alloc_[MAXPRIO];
00622     double  M_[MAXPRIO];
00623     int cnt_[MAXPRIO];      // # classes at prio of index
00624     int command(int argc, const char*const* argv);
00625 };
00626 
00627 static class WRR_CBQQueueClass : public TclClass {
00628 public:
00629     WRR_CBQQueueClass() : TclClass("Queue/CBQ/WRR") { }
00630     TclObject* create(int, const char*const*) {
00631         return (new WRR_CBQueue);
00632     }
00633 } class_wrr_cbq;
00634 
00635 int WRR_CBQueue::command(int argc, const char*const* argv)
00636 {
00637     Tcl& tcl = Tcl::instance();
00638     if (strcmp(argv[1], "insert-class") == 0) {
00639         CBQClass *cl = (CBQClass*)TclObject::lookup(argv[2]);
00640         if (cl == 0) {
00641             tcl.resultf("WRR-CBQ: no class object %s",
00642                 argv[2]);
00643             return (TCL_ERROR);
00644         }
00645         if (insert_class(cl) < 0) {
00646             tcl.resultf("WRR-CBQ: trouble inserting class %s",
00647                 argv[2]);
00648             return (TCL_ERROR);
00649         }
00650         return (TCL_OK);
00651     }
00652     return (CBQueue::command(argc, argv));
00653 }
00654 
00655 Packet *
00656 WRR_CBQueue::deque()
00657 {
00658 
00659     double now = Scheduler::instance().clock();
00660 
00661     CBQClass* first = NULL;
00662     CBQClass* eligible = NULL;
00663     CBQClass* next_eligible = NULL;
00664     CBQClass* cl;
00665 
00666     register int prio;
00667     int deficit, done;
00668     int none_found = 0;
00669 
00670     Packet* rval;
00671 
00672     /*
00673      * prio runs from 0 .. maxprio_
00674      *
00675      * round-robin through all the classes at priority 'prio'
00676      *      if any class is ok to send, resume it's queue
00677      * go on to next lowest priority (higher prio nuber) and repeat
00678      * [lowest priority number is the highest priority]
00679      */
00680 
00681     for (prio = 0; prio <= maxprio_; prio++) {
00682         // see if there is any class at this prio
00683         if ((cl = active_[prio]) == NULL) {
00684             // nobody at this prio level
00685             continue;
00686         }
00687                 /* 
00688                  * The WRR round for this priority level starts at deficit 0.
00689                  *  The round ends if some class is found that is ready
00690                  *  to send and has positive "bytes_alloc_".
00691                  * Status advances to deficit 1 if some class was found  
00692                  *  that was able to send except for insufficient
00693                  *  "bytes_alloc_".
00694                  * If status was deficit 1 at the end of the first round,
00695                  *  then status advances to deficit 2.
00696                  * Another round of WRR is then begun at deficit 2, looking
00697                  *  for a class to send even with insufficient "bytes_alloc_".
00698                  */
00699         deficit = done = 0;
00700         while (!done) {
00701                         // look for class at this priority level ok to send
00702             do {
00703                                 // set up "weight" for WRR
00704                 if (deficit < 2 && cl->bytes_alloc_ <= 0)
00705                     cl->bytes_alloc_ +=
00706                         (int)(cl->allotment_ * M_[cl->pri_]);
00707                                 // anything to send?
00708                 if (cl->demand()) {
00709                     if (first == NULL && cl->permit_borrowing_ && cl->lender_ != NULL)
00710                         first = cl;
00711                     if (!send_permitted(cl, now)) {
00712                                                 // not ok to send right now
00713                         cl->delayed(now);
00714                     } else {
00715                                                 // ok to send, if class has
00716                                                 //  enough "weight" for WRR.
00717                         int bytes = cl->bytes_alloc_;
00718                         if (bytes > 0 || deficit > 1) {
00719                             eligible = cl;
00720                             goto found;
00721                         } else
00722                             deficit = 1;
00723                     }
00724                 }
00725                 cl->bytes_alloc_ = 0;
00726                 cl = cl->peer_;
00727             } while (cl != active_[prio] && cl != 0);
00728             if (deficit == 1)
00729                 deficit = 2;
00730             else
00731                 done = 1;
00732         }
00733     }
00734         // did not find anyone so let first go
00735     if ((eligible == NULL) && first != NULL) {
00736         none_found = 1;
00737         eligible = first;
00738     }
00739 
00740 found:
00741     // do accounting
00742     if (eligible != NULL) {
00743         next_eligible = eligible->peer_;
00744         eligible->q_->resume();
00745         if (pending_pkt_ != NULL && !none_found) {
00746             // reduce our alloc
00747             // by the packet size.  If we're
00748             // still positive, we get to go again
00749             int bytes = eligible->bytes_alloc_;
00750             hdr_cmn* hdr = hdr_cmn::access(pending_pkt_);
00751             if (bytes > 0) {
00752                 eligible->bytes_alloc_ -= hdr->size();
00753             }
00754             bytes = eligible->bytes_alloc_;
00755             if (bytes > 0) {
00756                 next_eligible = eligible;
00757             }
00758             eligible->update(pending_pkt_, now);
00759             if (toplevel())
00760                 toplevel_departure(eligible, now);
00761         }
00762         active_[eligible->pri_] = next_eligible;
00763     }
00764     rval = pending_pkt_;
00765     pending_pkt_ = NULL;
00766 
00767     return (rval);
00768 }
00769 
00770 int
00771 WRR_CBQueue::insert_class(CBQClass *p)
00772 {
00773     if (CBQueue::insert_class(p) < 0)
00774         return (-1);
00775     ++cnt_[p->pri_];
00776     setM();
00777     return (0);
00778 }
00779 
00780 void
00781 WRR_CBQueue::setM()
00782 {
00783     int i;
00784     for (i = 0; i <= maxprio_; i++) {
00785         if (alloc_[i] > 0.0)
00786                         // allocate "cnt_[i] * maxpkt_" bytes to each
00787                         //  priority level:
00788                         M_[i] = cnt_[i] * maxpkt_ * 1.0 / alloc_[i];
00789                         // allocate "alloc_[i] * 2.0 * cnt_[i] * maxpkt_"
00790                         //  bytes to each priority level:
00791                         //  M_[i] = 2.0 * cnt_[i] * maxpkt_;
00792         else
00793             M_[i] = 0.0;
00794 
00795         if (M_[i] < 0.0) {
00796             fprintf(stderr, "M_[i]: %f, cnt_[i]: %d, maxpkt_: %d, alloc_[i]: %f\n",
00797                 M_[i], cnt_[i], maxpkt_, alloc_[i]);
00798             abort();
00799         }
00800 
00801     }
00802     return;
00803 }
00804 
00805 /******************** CBQClass definitions **********************/
00806 
00807 CBQClass::CBQClass() : cbq_(0), peer_(0), level_peer_(0), lender_(0),
00808     q_(0), qmon_(0), allotment_(0.0), maxidle_(-1.0), maxrate_(0.0),
00809     extradelay_(0.0), last_time_(0.0), undertime_(0.0), avgidle_(0.0),
00810     pri_(-1), level_(-1), delayed_(0), bytes_alloc_(0),
00811     permit_borrowing_(1)
00812 {
00813     /* maxidle_ is no longer bound; it is now a method interface */
00814     bind("priority_", &pri_);
00815     bind("level_", &level_);
00816     bind("extradelay_", &extradelay_);
00817     bind_bool("okborrow_", &permit_borrowing_);
00818 
00819     if (pri_ < 0 || pri_ > (MAXPRIO-1))
00820         abort();
00821 
00822     if (level_ <= 0 || level_ > MAXLEVEL)
00823         abort();
00824 }
00825 
00826 // why can't these two be inline (?)
00827 int
00828 CBQClass::demand()
00829 {
00830     return (qmon_->pkts() > 0);
00831 }
00832 
00833 int
00834 CBQClass::leaf()
00835 {
00836     return (level_ == LEAF_LEVEL);
00837 }
00838 
00839 /*
00840  * we are upstream from the queue
00841  * the queue should be unblocked if the downstream
00842  * cbq is not busy and blocked otherwise
00843  *
00844  * we get our packet from the classifier, because of
00845  * this the handler is NULL.  Besides the queue downstream
00846  * from us (Queue::recv) ignores the handler anyhow
00847  * 
00848  */
00849 void
00850 CBQClass::recv(Packet *pkt, Handler *h)
00851 {
00852     if (cbq_->toplevel()) {
00853         Scheduler* s;
00854         if ((s = &Scheduler::instance()) != NULL)
00855             cbq_->toplevel_arrival(this, s->clock());
00856     }
00857     send(pkt, h);   // queue packet downstream
00858     if (!cbq_->blocked()) {
00859         cbq_->sched();
00860     }
00861     return;
00862 }
00863 
00864 /*
00865  * update a class' statistics and all parent classes
00866  * up to the root
00867  */
00868 void CBQClass::update(Packet* p, double now)
00869 {
00870     double idle, avgidle;
00871 
00872     hdr_cmn* hdr = hdr_cmn::access(p);
00873     int pktsize = hdr->size();
00874 
00875     double tx_time = cbq_->link()->txtime(p);
00876     double fin_time = now + tx_time;
00877 
00878     idle = (fin_time - last_time_) - (pktsize / maxrate_);
00879     avgidle = avgidle_;
00880     avgidle += (idle - avgidle) / POWEROFTWO;
00881     if (maxidle_ < 0) {
00882         fprintf(stderr,
00883             "CBQClass: warning: maxidle_ not configured!\n");
00884     } else if (avgidle > maxidle_)
00885         avgidle = maxidle_;
00886     avgidle_ = avgidle;
00887 
00888     if (avgidle <= 0) {
00889         undertime_ = fin_time + tx_time *
00890             (1.0 / allotment_ - 1.0);
00891         undertime_ += (1-POWEROFTWO) * avgidle;
00892     }
00893     last_time_ = fin_time;
00894     // tail-recurse up to root of tree performing updates
00895     if (lender_)
00896         lender_->update(p, now);
00897 
00898     return;
00899 }
00900 
00901 /*
00902  * satisfied: is this class satisfied?
00903  */
00904 
00905 int
00906 CBQClass::satisfied(double now)
00907 {
00908     if (leaf()) {
00909         /* leaf is unsat if underlimit with backlog */
00910         if (undertime_ < now && demand())
00911             return (0);
00912         else
00913             return (1);
00914     }
00915     if (undertime_ < now && desc_with_demand())
00916         return (0);
00917 
00918     return (1);
00919 }
00920 
00921 /*
00922  * desc_with_demand: is there a descendant of this class with demand
00923  *  really, is there a leaf which is a descendant of me with
00924  *  a backlog
00925  */
00926 
00927 int
00928 CBQClass::desc_with_demand()
00929 {
00930     CBQClass *p = cbq_->level(LEAF_LEVEL);
00931     for (; p != NULL; p = p->level_peer_) {
00932         if (p->demand() && ancestor(p))
00933             return (1);
00934     }
00935     return (0);
00936 }
00937 
00938 /*
00939  * happens when a class is unable to send because it
00940  * is being regulated
00941  */
00942 
00943 void CBQClass::delayed(double now)
00944 {
00945     double delay = undertime_ - now + extradelay_;
00946 
00947     if (delay > 0 && !delayed_) {
00948         undertime_ += extradelay_;
00949         undertime_ -= (1-POWEROFTWO) * avgidle_;
00950         delayed_ = 1;
00951     }
00952 }
00953 
00954 /*
00955  * return 1 if we are an ancestor of p, 0 otherwise
00956  */
00957 int
00958 CBQClass::ancestor(CBQClass *p)
00959 {
00960     if (!p->permit_borrowing_ || p->lender_ == NULL)
00961         return (0);
00962     else if (p->lender_ == this)
00963         return (1);
00964     return (ancestor(p->lender_));
00965 }
00966 
00967 /*
00968  * change an allotment
00969  */
00970 void
00971 CBQClass::newallot(double bw)
00972 {
00973         if (allotment_ < 0)
00974                 allotment_ = 0;
00975         if (bw < 0)
00976                 bw = 0;
00977     maxrate_ = bw * ( cbq_->link()->bandwidth() / 8.0 );
00978     double diff = bw - allotment_;
00979     allotment_ = bw;
00980     cbq_->addallot(pri_, diff);
00981     return;
00982 }
00983 
00984 
00985 /*
00986  * OTcl Interface
00987  */
00988 /* 
00989  * $class1 parent $class2
00990  * $class1 borrow $class2
00991  * $class1 qdisc $queue
00992  * $class1 allot
00993  * $class1 allot new-bw
00994  */
00995 int CBQClass::command(int argc, const char*const* argv)
00996 {
00997     Tcl& tcl = Tcl::instance();
00998     if (argc == 2) {
00999         if (strcmp(argv[1], "allot") == 0) {
01000             tcl.resultf("%g", allotment_);
01001             return (TCL_OK);
01002         }
01003         if (strcmp(argv[1], "cbq") == 0) {
01004             if (cbq_ != NULL)
01005                 tcl.resultf("%s", cbq_->name());
01006             else
01007                 tcl.resultf("");
01008             return(TCL_OK);
01009         }
01010         if (strcmp(argv[1], "qdisc") == 0) {
01011             if (q_ != NULL)
01012                 tcl.resultf("%s", q_->name());
01013             else
01014                 tcl.resultf("");
01015             return (TCL_OK);
01016         }
01017         if (strcmp(argv[1], "qmon") == 0) {
01018             if (qmon_ != NULL)
01019                 tcl.resultf("%s", qmon_->name());
01020             else
01021                 tcl.resultf("");
01022             return (TCL_OK);
01023         }
01024     } else if (argc == 3) {
01025         // for now these are the same
01026         if ((strcmp(argv[1], "parent") == 0)) {
01027 
01028             if (strcmp(argv[2], "none") == 0) {
01029                 lender_ = NULL;
01030                 return (TCL_OK);
01031             }
01032             lender_ = (CBQClass*)TclObject::lookup(argv[2]);
01033             if (lender_ != NULL)
01034                 return (TCL_OK);
01035 
01036             return (TCL_ERROR);
01037         }
01038         if (strcmp(argv[1], "qdisc") == 0) {
01039             q_ = (Queue*) TclObject::lookup(argv[2]);
01040             if (q_ != NULL)
01041                 return (TCL_OK);
01042             tcl.resultf("couldn't find object %s",
01043                 argv[2]);
01044             return (TCL_ERROR);
01045         }
01046         if (strcmp(argv[1], "qmon") == 0) {
01047             qmon_ = (QueueMonitor*) TclObject::lookup(argv[2]);
01048             if (qmon_ != NULL)
01049                 return (TCL_OK);
01050             return (TCL_ERROR);
01051         }
01052         if (strcmp(argv[1], "allot") == 0) {
01053             double bw = atof(argv[2]);
01054             if (bw < 0.0)
01055                 return (TCL_ERROR);
01056             if (allotment_ != 0.0) {
01057                 tcl.resultf(" class %s already has allotment of %f!",
01058                         name(), allotment_);
01059                 return (TCL_ERROR);
01060             }
01061             allotment_ = bw;
01062             return (TCL_OK);
01063         }
01064         if (strcmp(argv[1], "newallot") == 0) {
01065             double bw = atof(argv[2]);
01066             if (bw < 0.0)
01067                 return (TCL_ERROR);
01068             newallot(bw);
01069             return (TCL_OK);
01070         }
01071         if (strcmp(argv[1], "maxidle") == 0) {
01072             double m = atof(argv[2]);
01073             if (m < 0.0) {
01074                 tcl.resultf("invalid maxidle value %s (must be non-negative)",
01075                         argv[2]);
01076                 return (TCL_ERROR);
01077             }
01078             maxidle_ = m;
01079             return (TCL_OK);
01080         }
01081     }
01082     return (Connector::command(argc, argv));
01083 }

Generated on Tue Mar 6 16:47:43 2007 for ns2 Network Simulator 2.29 by  doxygen 1.4.6