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
1.4.6