cbq.cc
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:26k
- /* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
- /*
- * Copyright (c) 1997 The Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the Network Research
- * Group at Lawrence Berkeley National Laboratory.
- * 4. Neither the name of the University nor of the Laboratory may be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
- #ifndef lint
- static const char rcsid[] =
- "@(#) $Header: /cvsroot/nsnam/ns-2/queue/cbq.cc,v 1.28 2005/07/27 01:13:44 tomh Exp $ (LBL)";
- #endif
- //
- // new version of cbq using the ns-2 fine-grain
- // objects. Also, re-orginaize CBQ to look more like how
- // its description reads in ToN v3n4 and simplify extraneous stuff -KF
- //
- // there is a 1-1 relationship between classes and queues, except
- // that internal nodes in the LS tree don't have queues
- //
- // Definitions:
- // overlimit:
- // recently used more than allocated link-sharing bandwidth
- // (in bytes/sec averaged over specified interval)
- //
- // level:
- // all leaves are at level 1
- // interior nodes are at a level 1 greater than
- // the highest level number of any of its children
- //
- // unsatisfied:
- // (leaf): underlimit and has demand
- // (interior): underlimit and has some descendant w/demand
- // [either a leaf or interior descendant]
- //
- // formal link-sharing:
- // class may continue unregulated if either:
- // 1> class is underlimit or at-limit
- // 2> class has a under(at)-limit ancestor at level i
- // and no unsatisfied classes at any levels < i
- //
- // ancestors-only link-sharing:
- // class may continue unregulated if either:
- // 1> class is under/at limit
- // 2> class has an UNDER-limit ancestor [at-limit not ok]
- //
- // top-level link-sharing:
- // class may continue unregulated if either:
- // 1> class is under/at limit
- // 2> class has an UNDER-limit ancestor with level
- // <= the value of "top-level"
- #include "queue-monitor.h"
- #include "queue.h"
- #include "delay.h"
- #define MAXPRIO 10 /* # priorities in scheduler */
- #define MAXLEVEL 32 /* max depth of link-share tree(s) */
- #define LEAF_LEVEL 1 /* level# for leaves */
- #define POWEROFTWO 16
- class CBQueue;
- class CBQClass : public Connector {
- public:
- friend class CBQueue;
- friend class WRR_CBQueue;
- CBQClass();
- int command(int argc, const char*const* argv);
- void recv(Packet*, Handler*); // from upstream classifier
- protected:
- void newallot(double); // change an allotment
- void update(Packet*, double); // update when sending pkt
- void delayed(double); // when overlim/can't borrow
- int satisfied(double); // satisfied?
- int demand(); // do I have demand?
- int leaf(); // am I a leaf class?
- int ancestor(CBQClass*p); // are we an ancestor of p?
- int desc_with_demand(); // any desc has demand?
- CBQueue* cbq_; // the CBQueue I'm part of
- CBQClass* peer_; // peer at same sched prio level
- CBQClass* level_peer_; // peer at same LS level
- CBQClass* lender_; // parent I can borrow from
- Queue* q_; // underlying queue
- QueueMonitor* qmon_; // monitor for the queue
- double allotment_; // frac of link bw
- double maxidle_; // bound on idle time
- double maxrate_; // bound on bytes/sec rate
- double extradelay_; // adjustment to delay
- double last_time_; // last xmit time this class
- double undertime_; // will become unsat/eligible
- double avgidle_; // EWMA of idle
- int pri_; // priority for scheduler
- int level_; // depth in link-sharing tree
- int delayed_; // boolean-was I delayed
- int bytes_alloc_; // for wrr only
- int permit_borrowing_; // ok to borrow?
- };
- class CBQueue : public Queue {
- public:
- CBQueue();
- void reset();
- void enque(Packet*) { abort(); }
- void recv(Packet*, Handler*);
- LinkDelay* link() const { return (link_); }
- CBQClass* level(int n) const { return levels_[n]; }
- Packet* deque();
- virtual int command(int argc, const char*const* argv);
- virtual void addallot(int, double) { }
- Packet* pending_pkt() const { return (pending_pkt_); }
- void sched();
- int toplevel() { // are we using toplevel?
- // return (eligible_ == &eligible_toplevel);
- return (eligible_ == TOPLEVEL);
- }
- void toplevel_arrival(CBQClass*, double);
- protected:
- Event intr_;
- int algorithm(const char *);
- virtual int insert_class(CBQClass*);
- int send_permitted(CBQClass*, double);
- CBQClass* find_lender(CBQClass*, double);
- void toplevel_departure(CBQClass*, double);
- CBQClass* last_lender_;
- Packet* pending_pkt_; // queued packet
- LinkDelay* link_; // managed link
- CBQClass* active_[MAXPRIO]; // classes at prio of index
- CBQClass* levels_[MAXLEVEL+1]; // LL of classes per level
- int maxprio_; // highest prio# seen
- int maxpkt_; // largest pkt (used by WRR)
- int maxlevel_; // highest level# seen
- int toplevel_; // for top-level LS
- // typedef int (CBQueue::*eligible_type_)(CBQClass*, double);
- // eligible_type_ eligible_; // eligible function
- enum eligible_type_ { NONE, FORMAL, ANCESTORS, TOPLEVEL };
- eligible_type_ eligible_;
- int eligible_formal(CBQClass*, double);
- int eligible_ancestors(CBQClass*, double) { return (1); }
- int eligible_toplevel(CBQClass* cl, double) {
- return(cl->level_ <= toplevel_);
- }
- };
- static class CBQQueueClass : public TclClass {
- public:
- CBQQueueClass() : TclClass("Queue/CBQ") { }
- TclObject* create(int, const char*const*) {
- return (new CBQueue);
- }
- } class_cbq;
- static class CBQClassClass : public TclClass {
- public:
- CBQClassClass() : TclClass("CBQClass") { }
- TclObject* create(int, const char*const*) {
- return (new CBQClass);
- }
- } class_cbqclass;
- CBQueue::CBQueue() : last_lender_(NULL), pending_pkt_(NULL), link_(NULL),
- maxprio_(-1), maxpkt_(-1), maxlevel_(-1), toplevel_(MAXLEVEL),
- // eligible_((eligible_type_)NULL)
- eligible_(NONE)
- {
- bind("maxpkt_", &maxpkt_);
- memset(active_, ' ', sizeof(active_));
- memset(levels_, ' ', sizeof(levels_));
- }
- /*
- * schedule ourselves, used by CBQClass::recv
- */
- void
- CBQueue::sched()
- {
- Scheduler& s = Scheduler::instance();
- blocked_ = 1;
- s.schedule(&qh_, &intr_, 0);
- }
- /*
- * invoked by passing a packet from one of our managed queues
- * basically provides a queue of one packet
- */
- void
- CBQueue::recv(Packet* p, Handler*)
- {
- if (pending_pkt_ != NULL)
- abort();
- blocked_ = 1;
- pending_pkt_ = p;
- }
- void
- CBQueue::reset()
- {
- // don't do anything
- // in particular, don't let Queue::reset() call
- // our deque() method
- }
- int
- CBQueue::algorithm(const char *arg)
- {
- if (*arg == '0' || (strcmp(arg, "ancestor-only") == 0)) {
- // eligible_ = &eligible_ancestors;
- eligible_ = ANCESTORS;
- return (1);
- } else if (*arg == '1' || (strcmp(arg, "top-level") == 0)) {
- // eligible_ = &eligible_toplevel;
- eligible_ = TOPLEVEL;
- return (1);
- } else if (*arg == '2' || (strcmp(arg, "formal") == 0)) {
- // eligible_ = &eligible_formal;
- eligible_ = FORMAL;
- return (1);
- } else if (*arg == '3' || (strcmp(arg, "old-formal") == 0)) {
- fprintf(stderr, "CBQ: old-formal LS not supportedn");
- return (-1);
- }
- return (-1);
- }
- /*
- *
- * toplevel_arrival: called only using TL link sharing on arrival
- * toplevel_departure: called only using TL link sharing on departure
- */
- void
- CBQueue::toplevel_departure(CBQClass *cl, double now)
- {
- if (toplevel_ >= last_lender_->level_) {
- if ((cl->qmon_->pkts() <= 1) ||
- last_lender_->undertime_ > now) {
- toplevel_ = MAXLEVEL;
- } else {
- toplevel_ = last_lender_->level_;
- }
- }
- }
- void
- CBQueue::toplevel_arrival(CBQClass *cl, double now)
- {
- if (toplevel_ > 1) {
- if (cl->undertime_ < now)
- toplevel_ = 1;
- else if (toplevel_ > 2 && cl->permit_borrowing_ && cl->lender_ != NULL) {
- if (cl->lender_->undertime_ < now)
- toplevel_ = 2;
- }
- }
- }
- /*
- * deque: this gets invoked by way of our downstream
- * (i.e. linkdelay) neighbor doing a 'resume' on us
- * via our handler (by Queue::resume()), or by our upstream
- * neighbor when it gives us a packet when we were
- * idle
- */
- Packet *
- CBQueue::deque()
- {
- Scheduler& s = Scheduler::instance();
- double now = s.clock();
- CBQClass* first = NULL;
- CBQClass* eligible = NULL;
- CBQClass* cl;
- register int prio;
- Packet* rval;
- int none_found = 0;
- /*
- * prio runs from 0 .. maxprio_
- *
- * round-robin through all the classes at priority 'prio'
- * if any class is ok to send, resume it's queue
- * go on to next lowest priority (higher prio nuber) and repeat
- * [lowest priority number is the highest priority]
- */
- for (prio = 0; prio <= maxprio_; prio++) {
- // see if there is any class at this prio
- if ((cl = active_[prio]) == NULL) {
- // nobody at this prio level
- continue;
- }
- // look for underlimit peer with something to send
- do {
- // anything to send?
- if (cl->demand()) {
- if (first == NULL && cl->permit_borrowing_ && cl->lender_ != NULL)
- first = cl;
- if (send_permitted(cl, now)) {
- // ok to send
- eligible = cl;
- goto found;
- } else {
- // not ok right now
- cl->delayed(now);
- }
- }
- cl = cl->peer_; // move to next at same prio
- } while (cl != active_[prio]);
- }
- // did not find anyone so let first go
- // eligible will be NULL at this point
- if (first != NULL) {
- none_found = 1;
- eligible = first;
- }
- found:
- if (eligible != NULL) {
- active_[eligible->pri_] = eligible->peer_;
- // eligible->q_->unblock();
- eligible->q_->resume(); // fills in pending
- if (pending_pkt_ && !none_found) {
- eligible->update(pending_pkt_, now);
- if (toplevel())
- toplevel_departure(eligible, now);
- }
- }
- rval = pending_pkt_;
- pending_pkt_ = NULL;
- return (rval);
- }
- /*
- * we are permitted to send if either
- * 1> we are not overlimit (ie we are underlimit or at limit)
- * 2> one of the varios algorithm-dependent conditions is met
- *
- * if we are permitted, who did we borrow from? [could be ourselves
- * if we were not overlimit]
- */
- int CBQueue::send_permitted(CBQClass* cl, double now)
- {
- if (cl->undertime_ < now) {
- cl->delayed_ = 0;
- last_lender_ = cl;
- return (1);
- } else if (cl->permit_borrowing_ &&
- (((cl = find_lender(cl, now)) != NULL))) {
- last_lender_ = cl;
- return (1);
- }
- return (0);
- }
- /*
- * find_lender(class, time)
- *
- * find a lender for the provided class according to the
- * various algorithms
- *
- */
- CBQClass*
- CBQueue::find_lender(CBQClass* cl, double now)
- {
- if ((!cl->permit_borrowing_) || ((cl = cl->lender_) == NULL))
- return (NULL); // no ancestor to borrow from
- while (cl != NULL) {
- // skip past overlimit ancestors
- // if using TL and we're above the TL limit
- // do early out
- if (cl->undertime_ > now) {
- if (toplevel() && cl->level_ > toplevel_)
- return (NULL);
- cl = cl->lender_;
- continue;
- }
- // found what may be an eligible
- // lender, check using per-algorithm eligibility
- // criteria
- // XXX we explicitly invoke this indirect method with
- // the "this" pointer because MS VC++ can't parse it
- // without it...
- // if ((this->*eligible_)(cl, now))
- // return (cl);
- switch (eligible_) {
- case TOPLEVEL:
- if (eligible_toplevel(cl, now))
- return (cl);
- break;
- case ANCESTORS:
- if (eligible_ancestors(cl, now))
- return (cl);
- break;
- case FORMAL:
- if (eligible_formal(cl, now))
- return (cl);
- break;
- default:
- fprintf(stderr, "Wrong eligible_n");
- abort();
- }
- cl = cl->lender_;
- }
- return (cl);
- }
- /*
- * rule #2 for formal link-sharing
- * class must have no unsatisfied classes below it
- */
- int
- CBQueue::eligible_formal(CBQClass *cl, double now)
- {
- int level;
- CBQClass *p;
- // check from leaf level to (cl->level - 1)
- for (level = LEAF_LEVEL; level < cl->level_; level++) {
- p = levels_[level];
- while (p != NULL) {
- if (!p->satisfied(now))
- return (0);
- p = p->level_peer_;
- }
- }
- return (1);
- }
- /*
- * insert a class into the cbq object
- */
- int
- CBQueue::insert_class(CBQClass *p)
- {
- p->cbq_ = this;
- /*
- * Add to circularly-linked list "active_"
- * of peers for the given priority.
- */
- if (p->pri_ < 0 || p->pri_ > (MAXPRIO-1)) {
- fprintf(stderr, "CBQ class %s has invalid pri %dn",
- p->name(), p->pri_);
- return (-1);
- }
- if (p->q_ != NULL) {
- // only leaf nodes (which have associated queues)
- // are scheduled
- if (active_[p->pri_] != NULL) {
- p->peer_ = active_[p->pri_]->peer_;
- active_[p->pri_]->peer_ = p;
- } else {
- p->peer_ = p;
- active_[p->pri_] = p;
- }
- if (p->pri_ > maxprio_)
- maxprio_ = p->pri_;
- }
- /*
- * Compute maxrate from allotment.
- * convert to bytes/sec
- * and store the highest prio# we've seen
- */
- if (p->allotment_ < 0.0 || p->allotment_ > 1.0) {
- fprintf(stderr, "CBQ class %s has invalid allot %fn",
- p->name(), p->allotment_);
- return (-1);
- }
- if (link_ == NULL) {
- fprintf(stderr, "CBQ obj %s has no link!n", name());
- return (-1);
- }
- if (link_->bandwidth() <= 0.0) {
- fprintf(stderr, "CBQ obj %s has invalid link bw %f on link %sn",
- name(), link_->bandwidth(), link_->name());
- return (-1);
- }
- p->maxrate_ = p->allotment_ * (link_->bandwidth() / 8.0);
- addallot(p->pri_, p->allotment_);
- /*
- * Add to per-level list
- * and store the highest level# we've seen
- */
- if (p->level_ <= 0 || p->level_ > MAXLEVEL) {
- fprintf(stderr, "CBQ class %s has invalid level %dn",
- p->name(), p->level_);
- return (-1);
- }
- p->level_peer_ = levels_[p->level_];
- levels_[p->level_] = p;
- if (p->level_ > maxlevel_)
- maxlevel_ = p->level_;
- /*
- * Check that parent and borrow linkage are acyclic.
- */
- #ifdef notdef
- check_for_cycles(CBQClass::parent);
- check_for_cycles(CBQClass::borrow);
- #endif
- return 0;
- }
- int CBQueue::command(int argc, const char*const* argv)
- {
- Tcl& tcl = Tcl::instance();
- if (argc == 3) {
- if (strcmp(argv[1], "insert-class") == 0) {
- CBQClass *cl = (CBQClass*)TclObject::lookup(argv[2]);
- if (cl == 0) {
- tcl.resultf("CBQ: no class object %s",
- argv[2]);
- return (TCL_ERROR);
- }
- if (insert_class(cl) < 0) {
- tcl.resultf("CBQ: trouble inserting class %s",
- argv[2]);
- return (TCL_ERROR);
- }
- return (TCL_OK);
- }
- if (strcmp(argv[1], "link") == 0) {
- LinkDelay* del = (LinkDelay*)TclObject::lookup(argv[2]);
- if (del == 0) {
- tcl.resultf("CBQ: no LinkDelay object %s",
- argv[2]);
- return(TCL_ERROR);
- }
- link_ = del;
- return (TCL_OK);
- }
- if (strcmp(argv[1], "algorithm") == 0) {
- if (algorithm(argv[2]) < 0)
- return (TCL_ERROR);
- return (TCL_OK);
- }
- }
- return (Queue::command(argc, argv));
- }
- class WRR_CBQueue : public CBQueue {
- public:
- WRR_CBQueue() {
- memset(M_, ' ', sizeof(M_));
- memset(alloc_, ' ', sizeof(alloc_));
- memset(cnt_, ' ', sizeof(cnt_));
- }
- void addallot(int prio, double diff) {
- alloc_[prio] += diff;
- setM();
- }
- protected:
- Packet *deque();
- int insert_class(CBQClass*);
- void setM();
- double alloc_[MAXPRIO];
- double M_[MAXPRIO];
- int cnt_[MAXPRIO]; // # classes at prio of index
- int command(int argc, const char*const* argv);
- };
- static class WRR_CBQQueueClass : public TclClass {
- public:
- WRR_CBQQueueClass() : TclClass("Queue/CBQ/WRR") { }
- TclObject* create(int, const char*const*) {
- return (new WRR_CBQueue);
- }
- } class_wrr_cbq;
- int WRR_CBQueue::command(int argc, const char*const* argv)
- {
- Tcl& tcl = Tcl::instance();
- if (strcmp(argv[1], "insert-class") == 0) {
- CBQClass *cl = (CBQClass*)TclObject::lookup(argv[2]);
- if (cl == 0) {
- tcl.resultf("WRR-CBQ: no class object %s",
- argv[2]);
- return (TCL_ERROR);
- }
- if (insert_class(cl) < 0) {
- tcl.resultf("WRR-CBQ: trouble inserting class %s",
- argv[2]);
- return (TCL_ERROR);
- }
- return (TCL_OK);
- }
- return (CBQueue::command(argc, argv));
- }
- Packet *
- WRR_CBQueue::deque()
- {
- double now = Scheduler::instance().clock();
- CBQClass* first = NULL;
- CBQClass* eligible = NULL;
- CBQClass* next_eligible = NULL;
- CBQClass* cl;
- register int prio;
- int deficit, done;
- int none_found = 0;
- Packet* rval;
- /*
- * prio runs from 0 .. maxprio_
- *
- * round-robin through all the classes at priority 'prio'
- * if any class is ok to send, resume it's queue
- * go on to next lowest priority (higher prio nuber) and repeat
- * [lowest priority number is the highest priority]
- */
- for (prio = 0; prio <= maxprio_; prio++) {
- // see if there is any class at this prio
- if ((cl = active_[prio]) == NULL) {
- // nobody at this prio level
- continue;
- }
- /*
- * The WRR round for this priority level starts at deficit 0.
- * The round ends if some class is found that is ready
- * to send and has positive "bytes_alloc_".
- * Status advances to deficit 1 if some class was found
- * that was able to send except for insufficient
- * "bytes_alloc_".
- * If status was deficit 1 at the end of the first round,
- * then status advances to deficit 2.
- * Another round of WRR is then begun at deficit 2, looking
- * for a class to send even with insufficient "bytes_alloc_".
- */
- deficit = done = 0;
- while (!done) {
- // look for class at this priority level ok to send
- do {
- // set up "weight" for WRR
- if (deficit < 2 && cl->bytes_alloc_ <= 0)
- cl->bytes_alloc_ +=
- (int)(cl->allotment_ * M_[cl->pri_]);
- // anything to send?
- if (cl->demand()) {
- if (first == NULL && cl->permit_borrowing_ && cl->lender_ != NULL)
- first = cl;
- if (!send_permitted(cl, now)) {
- // not ok to send right now
- cl->delayed(now);
- } else {
- // ok to send, if class has
- // enough "weight" for WRR.
- int bytes = cl->bytes_alloc_;
- if (bytes > 0 || deficit > 1) {
- eligible = cl;
- goto found;
- } else
- deficit = 1;
- }
- }
- cl->bytes_alloc_ = 0;
- cl = cl->peer_;
- } while (cl != active_[prio] && cl != 0);
- if (deficit == 1)
- deficit = 2;
- else
- done = 1;
- }
- }
- // did not find anyone so let first go
- if ((eligible == NULL) && first != NULL) {
- none_found = 1;
- eligible = first;
- }
- found:
- // do accounting
- if (eligible != NULL) {
- next_eligible = eligible->peer_;
- eligible->q_->resume();
- if (pending_pkt_ != NULL && !none_found) {
- // reduce our alloc
- // by the packet size. If we're
- // still positive, we get to go again
- int bytes = eligible->bytes_alloc_;
- hdr_cmn* hdr = hdr_cmn::access(pending_pkt_);
- if (bytes > 0) {
- eligible->bytes_alloc_ -= hdr->size();
- }
- bytes = eligible->bytes_alloc_;
- if (bytes > 0) {
- next_eligible = eligible;
- }
- eligible->update(pending_pkt_, now);
- if (toplevel())
- toplevel_departure(eligible, now);
- }
- active_[eligible->pri_] = next_eligible;
- }
- rval = pending_pkt_;
- pending_pkt_ = NULL;
- return (rval);
- }
- int
- WRR_CBQueue::insert_class(CBQClass *p)
- {
- if (CBQueue::insert_class(p) < 0)
- return (-1);
- ++cnt_[p->pri_];
- setM();
- return (0);
- }
- void
- WRR_CBQueue::setM()
- {
- int i;
- for (i = 0; i <= maxprio_; i++) {
- if (alloc_[i] > 0.0)
- // allocate "cnt_[i] * maxpkt_" bytes to each
- // priority level:
- M_[i] = cnt_[i] * maxpkt_ * 1.0 / alloc_[i];
- // allocate "alloc_[i] * 2.0 * cnt_[i] * maxpkt_"
- // bytes to each priority level:
- // M_[i] = 2.0 * cnt_[i] * maxpkt_;
- else
- M_[i] = 0.0;
- if (M_[i] < 0.0) {
- fprintf(stderr, "M_[i]: %f, cnt_[i]: %d, maxpkt_: %d, alloc_[i]: %fn",
- M_[i], cnt_[i], maxpkt_, alloc_[i]);
- abort();
- }
- }
- return;
- }
- /******************** CBQClass definitions **********************/
- CBQClass::CBQClass() : cbq_(0), peer_(0), level_peer_(0), lender_(0),
- q_(0), qmon_(0), allotment_(0.0), maxidle_(-1.0), maxrate_(0.0),
- extradelay_(0.0), last_time_(0.0), undertime_(0.0), avgidle_(0.0),
- pri_(-1), level_(-1), delayed_(0), bytes_alloc_(0),
- permit_borrowing_(1)
- {
- /* maxidle_ is no longer bound; it is now a method interface */
- bind("priority_", &pri_);
- bind("level_", &level_);
- bind("extradelay_", &extradelay_);
- bind_bool("okborrow_", &permit_borrowing_);
- if (pri_ < 0 || pri_ > (MAXPRIO-1))
- abort();
- if (level_ <= 0 || level_ > MAXLEVEL)
- abort();
- }
- // why can't these two be inline (?)
- int
- CBQClass::demand()
- {
- return (qmon_->pkts() > 0);
- }
- int
- CBQClass::leaf()
- {
- return (level_ == LEAF_LEVEL);
- }
- /*
- * we are upstream from the queue
- * the queue should be unblocked if the downstream
- * cbq is not busy and blocked otherwise
- *
- * we get our packet from the classifier, because of
- * this the handler is NULL. Besides the queue downstream
- * from us (Queue::recv) ignores the handler anyhow
- *
- */
- void
- CBQClass::recv(Packet *pkt, Handler *h)
- {
- if (cbq_->toplevel()) {
- Scheduler* s;
- if ((s = &Scheduler::instance()) != NULL)
- cbq_->toplevel_arrival(this, s->clock());
- }
- send(pkt, h); // queue packet downstream
- if (!cbq_->blocked()) {
- cbq_->sched();
- }
- return;
- }
- /*
- * update a class' statistics and all parent classes
- * up to the root
- */
- void CBQClass::update(Packet* p, double now)
- {
- double idle, avgidle;
- hdr_cmn* hdr = hdr_cmn::access(p);
- int pktsize = hdr->size();
- double tx_time = cbq_->link()->txtime(p);
- double fin_time = now + tx_time;
- idle = (fin_time - last_time_) - (pktsize / maxrate_);
- avgidle = avgidle_;
- avgidle += (idle - avgidle) / POWEROFTWO;
- if (maxidle_ < 0) {
- fprintf(stderr,
- "CBQClass: warning: maxidle_ not configured!n");
- } else if (avgidle > maxidle_)
- avgidle = maxidle_;
- avgidle_ = avgidle;
- if (avgidle <= 0) {
- undertime_ = fin_time + tx_time *
- (1.0 / allotment_ - 1.0);
- undertime_ += (1-POWEROFTWO) * avgidle;
- }
- last_time_ = fin_time;
- // tail-recurse up to root of tree performing updates
- if (lender_)
- lender_->update(p, now);
- return;
- }
- /*
- * satisfied: is this class satisfied?
- */
- int
- CBQClass::satisfied(double now)
- {
- if (leaf()) {
- /* leaf is unsat if underlimit with backlog */
- if (undertime_ < now && demand())
- return (0);
- else
- return (1);
- }
- if (undertime_ < now && desc_with_demand())
- return (0);
- return (1);
- }
- /*
- * desc_with_demand: is there a descendant of this class with demand
- * really, is there a leaf which is a descendant of me with
- * a backlog
- */
- int
- CBQClass::desc_with_demand()
- {
- CBQClass *p = cbq_->level(LEAF_LEVEL);
- for (; p != NULL; p = p->level_peer_) {
- if (p->demand() && ancestor(p))
- return (1);
- }
- return (0);
- }
- /*
- * happens when a class is unable to send because it
- * is being regulated
- */
- void CBQClass::delayed(double now)
- {
- double delay = undertime_ - now + extradelay_;
- if (delay > 0 && !delayed_) {
- undertime_ += extradelay_;
- undertime_ -= (1-POWEROFTWO) * avgidle_;
- delayed_ = 1;
- }
- }
- /*
- * return 1 if we are an ancestor of p, 0 otherwise
- */
- int
- CBQClass::ancestor(CBQClass *p)
- {
- if (!p->permit_borrowing_ || p->lender_ == NULL)
- return (0);
- else if (p->lender_ == this)
- return (1);
- return (ancestor(p->lender_));
- }
- /*
- * change an allotment
- */
- void
- CBQClass::newallot(double bw)
- {
- if (allotment_ < 0)
- allotment_ = 0;
- if (bw < 0)
- bw = 0;
- maxrate_ = bw * ( cbq_->link()->bandwidth() / 8.0 );
- double diff = bw - allotment_;
- allotment_ = bw;
- cbq_->addallot(pri_, diff);
- return;
- }
- /*
- * OTcl Interface
- */
- /*
- * $class1 parent $class2
- * $class1 borrow $class2
- * $class1 qdisc $queue
- * $class1 allot
- * $class1 allot new-bw
- */
- int CBQClass::command(int argc, const char*const* argv)
- {
- Tcl& tcl = Tcl::instance();
- if (argc == 2) {
- if (strcmp(argv[1], "allot") == 0) {
- tcl.resultf("%g", allotment_);
- return (TCL_OK);
- }
- if (strcmp(argv[1], "cbq") == 0) {
- if (cbq_ != NULL)
- tcl.resultf("%s", cbq_->name());
- else
- tcl.resultf("");
- return(TCL_OK);
- }
- if (strcmp(argv[1], "qdisc") == 0) {
- if (q_ != NULL)
- tcl.resultf("%s", q_->name());
- else
- tcl.resultf("");
- return (TCL_OK);
- }
- if (strcmp(argv[1], "qmon") == 0) {
- if (qmon_ != NULL)
- tcl.resultf("%s", qmon_->name());
- else
- tcl.resultf("");
- return (TCL_OK);
- }
- } else if (argc == 3) {
- // for now these are the same
- if ((strcmp(argv[1], "parent") == 0)) {
- if (strcmp(argv[2], "none") == 0) {
- lender_ = NULL;
- return (TCL_OK);
- }
- lender_ = (CBQClass*)TclObject::lookup(argv[2]);
- if (lender_ != NULL)
- return (TCL_OK);
- return (TCL_ERROR);
- }
- if (strcmp(argv[1], "qdisc") == 0) {
- q_ = (Queue*) TclObject::lookup(argv[2]);
- if (q_ != NULL)
- return (TCL_OK);
- tcl.resultf("couldn't find object %s",
- argv[2]);
- return (TCL_ERROR);
- }
- if (strcmp(argv[1], "qmon") == 0) {
- qmon_ = (QueueMonitor*) TclObject::lookup(argv[2]);
- if (qmon_ != NULL)
- return (TCL_OK);
- return (TCL_ERROR);
- }
- if (strcmp(argv[1], "allot") == 0) {
- double bw = atof(argv[2]);
- if (bw < 0.0)
- return (TCL_ERROR);
- if (allotment_ != 0.0) {
- tcl.resultf(" class %s already has allotment of %f!",
- name(), allotment_);
- return (TCL_ERROR);
- }
- allotment_ = bw;
- return (TCL_OK);
- }
- if (strcmp(argv[1], "newallot") == 0) {
- double bw = atof(argv[2]);
- if (bw < 0.0)
- return (TCL_ERROR);
- newallot(bw);
- return (TCL_OK);
- }
- if (strcmp(argv[1], "maxidle") == 0) {
- double m = atof(argv[2]);
- if (m < 0.0) {
- tcl.resultf("invalid maxidle value %s (must be non-negative)",
- argv[2]);
- return (TCL_ERROR);
- }
- maxidle_ = m;
- return (TCL_OK);
- }
- }
- return (Connector::command(argc, argv));
- }