vq.cc

Go to the documentation of this file.
00001 /* -*-  Mode:C++; c-basic-offset:4; tab-width:4; indent-tabs-mode:t -*- */
00002 /*
00003  * Copyright (c) 1994 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 Computer Systems
00017  *  Engineering Group at Lawrence Berkeley 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 /* Marking scheme proposed by Kunniyur and Srikant in "Decentralized Adaptive
00036 *    ECN Algorithms" published in the Proceedings of Infocom2000, Tel-Aviv, 
00037 *    Israel, March 2000.
00038 * 
00039 * Basic Idea of the AVQ (Adaptive Virtual Queue) Algorithm
00040 * --------------------------------------------------------
00041 * 
00042 * The scheme involves having a virtual queue at each link with the same
00043 * arrival rate as the original queue, but with the service rate scaled
00044 * down by some factor (ecnlim_). The buffer capacity remains the same,
00045 * but if needed we can also scale the buffer capacity by come constant
00046 * (buflim_). When a packet is dropped from the virtual queue, mark a
00047 * packet at the front of the real queue. Although in this implemetation,
00048 * a "drop-tail" function is used in the VIRTUAL QUEUE to mark packets in
00049 * the original queue, any algorithm (like RED) can be used in the
00050 * virtual queue to signal congestion. The capacity of the virtual queue
00051 * is adapted periodically to guarantee loss-free service. 
00052 * 
00053 * The update of the virtual capacity (capacity of the virtual queue) is
00054 * done using a token-bucket. For implentation details, please refer to
00055 * the Sigcomm 2001 paper titled "Analysis and Design of an Adaptive
00056 * Virtual Queue (AVQ) algorithm for Active Queue Management (AQM). 
00057 * 
00058 * The virtual queue length can be taken in packets or in bytes. If the
00059 * packet sizes are not fixed, then it is recommended to use bytes
00060 * instead of packets as units of length. If the variable (qib_) is set,
00061 * then the virtual queue is measured in bytes, else it is measured in
00062 * packets and the mean packet size is set to 1000. 
00063 * 
00064 *                          -Srisankar
00065 * 
00066 * ********************************************************************  
00067 * Options in the AVQ algorithm code:
00068 * ----------------------------------
00069 * 
00070 * * ecnlim_ -> This parameter specifies the capacity of the virtual 
00071 *              queue as a fraction of the capcity of the original queue.
00072 * 
00073 * * buflim_ -> This parameter specifies the buffer capacity of the
00074 *              virtual queue as a fraction of the buffer capacity of the
00075 *              original queue.
00076 * 
00077 * * queue_in_bytes_ -> Indicates whether you want to measure the queue
00078 *                      in bytes or packets. 
00079 * 
00080 * * markpkts_ -> Set to 1 if router wants to emply marking for that
00081 *                link. Set to 0, if dropping is preferred. 
00082 * 
00083 * * markfront_ -> Set to 1 if mark from front policy is employed. Usually
00084 *                 set to zero.   
00085 * 
00086 * * mean_pktsize- -> The mean packet size is set to 1000.
00087 * 
00088 * * gamma -> This parameter specifies the desired arrival rate at the link.
00089 * 
00090 * ********************************************************************  
00091 */
00092 
00093 #ifndef lint
00094 static const char rcsid[] =
00095     "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/queue/vq.cc,v 1.5 2003/01/28 19:50:51 sfloyd Exp $ (LBL)";
00096 #endif
00097 #include "flags.h"
00098 #include "delay.h"
00099 #include "vq.h"
00100 #include "math.h"
00101 
00102 static class VqClass : public TclClass {
00103 public:
00104     VqClass() : TclClass("Queue/Vq") {}
00105     TclObject* create(int argc, const char*const* argv) {
00106         if (argc==5) 
00107             return (new Vq(argv[4]));
00108         else
00109             return (new Vq("Drop"));
00110     }
00111 } class_vq;
00112 
00113 Vq::Vq(const char * trace) : link_(NULL), EDTrace(NULL), tchan_(0){
00114     q_ = new PacketQueue;
00115     pq_ = q_;
00116     bind_bool("drop_front_", &drop_front_);
00117     bind("ecnlim_", &ecnlim_); //  = ctilde/c ; Initial value set to 0.8
00118     bind("buflim_", &buflim_); /* Fraction of the original buffer that the VQ buffer has. Default is 1.0 */
00119     bind_bool("queue_in_bytes_", &qib_);     // boolean: q in bytes?
00120     bind("gamma_", &gamma_); // Defines the utilization. Default is 0.98
00121     bind_bool("markpkts_", &markpkts_); /* Whether to mark or drop?  Default is drop */
00122     bind_bool("markfront_", &markfront_); /* Mark from front?  Deafult is false */
00123     bind("mean_pktsize_", &mean_pktsize_);  // avg pkt size
00124     bind("curq_", &curq_);          // current queue size
00125     
00126     vq_len = 0.0;
00127     prev_time = 0.0;
00128     vqprev_time = 0.0;
00129     alpha2 = 0.15; // Determines how fast we adapt at the link
00130     Pktdrp = 0; /* Useful if we are dropping pkts instead of marking */
00131     firstpkt = 1;
00132     qlength = 0; /* Tracks the queue length */
00133 }
00134 
00135 int Vq::command(int argc, const char*const* argv) {
00136     Tcl& tcl = Tcl::instance();
00137   if (argc == 3) {
00138       if (strcmp(argv[1], "link") == 0) {
00139           LinkDelay* del = (LinkDelay*)TclObject::lookup(argv[2]);
00140           if (del == 0) {
00141               return(TCL_ERROR);
00142           }
00143           // set capacity now
00144             link_ = del;
00145           c_ = del->bandwidth();
00146           return (TCL_OK);
00147       }
00148       if (!strcmp(argv[1], "packetqueue-attach")) {
00149           delete q_;
00150           if (!(q_ = (PacketQueue*) TclObject::lookup(argv[2])))
00151               return (TCL_ERROR);
00152           else {
00153               pq_ = q_;
00154               return (TCL_OK);
00155           }
00156       }
00157         // attach a file for variable tracing
00158         if (strcmp(argv[1], "attach") == 0) {
00159             int mode;
00160             const char* id = argv[2];
00161             tchan_ = Tcl_GetChannel(tcl.interp(), (char*)id, &mode);
00162             if (tchan_ == 0) {
00163                 tcl.resultf("Vq: trace: can't attach %s for writing", id);
00164                 return (TCL_ERROR);
00165             }
00166             return (TCL_OK);
00167         }
00168   }
00169   return Queue::command(argc, argv);
00170 }
00171 
00172 void Vq::enque(Packet* p)
00173 {
00174     q_->enque(p);
00175     hdr_cmn* ch = hdr_cmn::access(p);
00176     qlength = qlength + (qib_ * ch->size()) + (1 - qib_);
00177     
00178     if(firstpkt){
00179         /* Changing c_ so that it is measured in packets per second */
00180         /* Assuming packets are fixed size with 1000 bytes */
00181         
00182         if(qib_) c_ = c_ / (8.0);
00183         else c_ = c_ / (8.0 * mean_pktsize_);
00184         firstpkt = 0;
00185         ctilde = c_ * ecnlim_;
00186         prev_time = Scheduler::instance().clock();
00187         vqprev_time = Scheduler::instance().clock();
00188     }
00189 
00190     /* Update the Virtual Queue length */   
00191     curr_time = Scheduler::instance().clock();
00192     vq_len = vq_len - (ctilde * (curr_time - vqprev_time));
00193     vqprev_time = curr_time;
00194     if(vq_len < 0.0) vq_len = 0.0;
00195     vq_len = vq_len + qib_ * ch->size() + (1 - qib_);
00196     
00197     /* checkPacketForECN() returns 1 if we need to mark or drop a packet*/
00198     
00199     if(checkPacketForECN()){
00200         if(markpkts_)
00201             markPacketForECN(p);
00202         else
00203             dropPacketForECN(p);
00204     }
00205     
00206     /* Adaptation of the virtual capacity, tilde(C) */
00207     /* Use the token bucket system  */
00208     /* Scale alpha appropriately if qib_ is set */
00209     
00210     if(Pktdrp == 0){ // Do the adaptation
00211         /* Pktdrp = 0 always when marking */
00212         ctilde = ctilde + alpha2*gamma_*c_*(curr_time - prev_time) - alpha2*(1.0 - qib_) - alpha2*qib_*ch->size();
00213         if(ctilde > c_) ctilde = c_;
00214         if(ctilde <0.0) ctilde = 0.0;
00215         prev_time = curr_time;
00216     }
00217     else{ // No adaptation and reset Pktdrp
00218         Pktdrp = 0;
00219     }
00220     
00221     if (qlength > qlim_*( 1 - qib_ + qib_*mean_pktsize_)) {
00222         if (drop_front_) { /* remove from head of queue */
00223             if(q_->length() > 0){
00224                 Packet *pp = q_->head();
00225                 qlength = qlength - qib_ * ch->size() - (1 - qib_);
00226                 q_->remove(pp); 
00227                 drop(pp);
00228             }
00229         } 
00230         else {
00231             q_->remove(p);
00232             qlength = qlength - qib_ * ch->size() - (1 - qib_);
00233             drop(p);
00234         }
00235     }
00236     curq_ = qlength;
00237 }
00238 
00239 /* This is a simple DropTail on the VQ. However, one Can add 
00240    any AQM scheme on VQ here */
00241 int Vq::checkPacketForECN(){
00242     if(vq_len > (buflim_ * qlim_ * ( 1 - qib_ + qib_*mean_pktsize_))){
00243         return 1;
00244     }
00245     else{
00246         return 0;
00247     }
00248 }
00249 
00250 /* Implements a simple mark-tail/mark-front here. If needed other
00251    mechanism (like mark-random ) can also be implemented */ 
00252 void  Vq::markPacketForECN(Packet* pkt)
00253 {
00254     /* Update the VQ length */
00255     hdr_cmn* ch = hdr_cmn::access(pkt);
00256     vq_len = vq_len - qib_ * ch->size() - (1.0 - qib_);
00257     if(vq_len < 0.0) vq_len = 0.0;
00258     
00259     if(markfront_){ 
00260         Packet *pp = q_->head();
00261         hdr_flags* hf = hdr_flags::access(pp);
00262         if(hf->ect() == 1)  // ECN capable flow
00263             hf->ce() = 1; // Mark the TCP Flow;
00264     }
00265     else{ 
00266         /* Mark the current packet and forget about it */
00267         hdr_flags* hdr = hdr_flags::access(pkt);
00268         if(hdr->ect() == 1)  // ECN capable flow
00269             hdr->ce() = 1; // For TCP Flows
00270     }
00271 }   
00272 
00273 /* Implements a simple drop-tail/drop-front here. If needed other
00274    mechanism (like drop-random ) can also be implemented */ 
00275 void Vq::dropPacketForECN(Packet* pkt) 
00276 {
00277     /* Update the VQ length */
00278     hdr_cmn* ch = hdr_cmn::access(pkt);
00279     vq_len = vq_len - qib_ * ch->size() - (1.0 - qib_);
00280     if(vq_len < 0.0) vq_len = 0.0;
00281 
00282     if(drop_front_){
00283         /* If drop from front is enabled, then deque a 
00284            packet from the front of the queue and drop it ...
00285            Usually not recommended */ 
00286         if(q_->length() > 0 ){
00287             Packet *pp = q_->head();
00288             qlength = qlength - qib_ * ch->size() - (1 - qib_);
00289             q_->remove(pp); /* The queue length is taken care of in
00290                                          in the deque program */
00291             drop(pp);
00292 
00293         }
00294     }
00295     else{
00296         q_->remove(pkt);
00297         qlength = qlength - qib_ * ch->size() - (1 - qib_);
00298         drop(pkt);
00299     }
00300     
00301     /* If one is dropping packets, one needs to be careful about 
00302        measuring the total arrival rate to calculate the virtual
00303        capacity, tilde(C). In this case, the arrival rate that is
00304        taken into the adaptation algorithm is the accepted rate and
00305        not the offered rate. */
00306     Pktdrp = 1; // Do not update tilde(C)
00307     
00308 }   
00309 
00310 Packet* Vq::deque()
00311 {
00312     if((q_->length() > 0)){
00313         Packet *ppp = q_->deque();
00314         hdr_cmn* ch = hdr_cmn::access(ppp);
00315         qlength = qlength - qib_ * ch->size() - (1 - qib_);
00316         curq_ = qlength;
00317         return ppp;
00318     }
00319     else return q_->deque();
00320 }
00321 
00322 /*
00323  * Routine called by TracedVar facility when variables change values.
00324  * Currently used to trace value of 
00325  * the instantaneous queue size seen by arriving packets.
00326  * Note that the tracing of each var must be enabled in tcl to work.
00327  */
00328 
00329 void Vq::trace(TracedVar* v)
00330 {
00331     char wrk[500], *p;
00332 
00333     if ((p = strstr(v->name(), "curq")) == NULL) {
00334         fprintf(stderr, "Vq:unknown trace var %s\n", v->name());
00335         return;
00336     }
00337 
00338     if (tchan_) {
00339         int n;
00340         double t = Scheduler::instance().clock();
00341         // XXX: be compatible with nsv1 RED trace entries
00342         if (*p == 'c') {
00343             sprintf(wrk, "Q %g %d", t, int(*((TracedInt*) v)));
00344         } else {
00345             sprintf(wrk, "%c %g %g", *p, t, double(*((TracedDouble*) v)));
00346         }
00347         n = strlen(wrk);
00348         wrk[n] = '\n'; 
00349         wrk[n+1] = 0;
00350         (void)Tcl_Write(tchan_, wrk, n+1);
00351     }
00352     return; 
00353 }
00354 
00355 
00356 
00357 
00358 
00359 
00360 
00361 

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