Main Page | Namespace List | Class Hierarchy | Alphabetical List | Compound List | File List | Compound Members | File Members

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

Generated on Tue Apr 20 12:14:11 2004 for NS2.26SourcesOriginal by doxygen 1.3.3