red.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) 1990-1997 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  * Here is one set of parameters from one of Sally's simulations
00036  * (this is from tcpsim, the older simulator):
00037  * 
00038  * ed [ q_weight=0.002 thresh=5 linterm=30 maxthresh=15
00039  *         mean_pktsize=500 dropmech=random-drop queue-size=60
00040  *         plot-file=none bytes=false doubleq=false dqthresh=50 
00041  *     wait=true ]
00042  * 
00043  * 1/"linterm" is the max probability of dropping a packet. 
00044  * There are different options that make the code
00045  * more messy that it would otherwise be.  For example,
00046  * "doubleq" and "dqthresh" are for a queue that gives priority to
00047  *   small (control) packets, 
00048  * "bytes" indicates whether the queue should be measured in bytes 
00049  *   or in packets, 
00050  * "dropmech" indicates whether the drop function should be random-drop 
00051  *   or drop-tail when/if the queue overflows, and 
00052  *   the commented-out Holt-Winters method for computing the average queue 
00053  *   size can be ignored.
00054  * "wait" indicates whether the gateway should wait between dropping
00055  *   packets.
00056  */
00057 
00058 #ifndef lint
00059 static const char rcsid[] =
00060     "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/queue/red.cc,v 1.80 2004/10/28 23:35:37 haldar Exp $ (LBL)";
00061 #endif
00062 
00063 #include <math.h>
00064 #include <sys/types.h>
00065 #include "config.h"
00066 #include "template.h"
00067 #include "random.h"
00068 #include "flags.h"
00069 #include "delay.h"
00070 #include "red.h"
00071 
00072 static class REDClass : public TclClass {
00073 public:
00074     REDClass() : TclClass("Queue/RED") {}
00075     TclObject* create(int argc, const char*const* argv) {
00076         //printf("creating RED Queue. argc = %d\n", argc);
00077         
00078         //mod to enable RED to take arguments
00079         if (argc==5) 
00080             return (new REDQueue(argv[4]));
00081         else
00082             return (new REDQueue("Drop"));
00083     }
00084 } class_red;
00085 
00086 /* Strangely this didn't work. 
00087  * Seg faulted for child classes.
00088 REDQueue::REDQueue() { 
00089     REDQueue("Drop");
00090 }
00091 */
00092 
00093 /*
00094  * modified to enable instantiation with special Trace objects - ratul
00095  */
00096 REDQueue::REDQueue(const char * trace) : link_(NULL), de_drop_(NULL), EDTrace(NULL), tchan_(0), idle_(1)
00097 {
00098     initParams();
00099     
00100     //  printf("Making trace type %s\n", trace);
00101     if (strlen(trace) >=20) {
00102         printf("trace type too long - allocate more space to traceType in red.h and recompile\n");
00103         exit(0);
00104     }
00105     strcpy(traceType, trace);
00106     bind_bool("bytes_", &edp_.bytes);       // boolean: use bytes?
00107     bind_bool("queue_in_bytes_", &qib_);        // boolean: q in bytes?
00108     //  _RENAMED("queue-in-bytes_", "queue_in_bytes_");
00109 
00110     bind("thresh_", &edp_.th_min_pkts);         // minthresh
00111     bind("thresh_queue_", &edp_.th_min);
00112     bind("maxthresh_", &edp_.th_max_pkts);      // maxthresh
00113     bind("minthresh_queue_", &edp_.th_max);
00114     bind("mean_pktsize_", &edp_.mean_pktsize);  // avg pkt size
00115     bind("idle_pktsize_", &edp_.idle_pktsize);  // avg pkt size for idles
00116     bind("q_weight_", &edp_.q_w);           // for EWMA
00117     bind("adaptive_", &edp_.adaptive);          // 1 for adaptive red
00118     bind("cautious_", &edp_.cautious);          // 1 for cautious marking
00119     bind("alpha_", &edp_.alpha);            // adaptive red param
00120     bind("beta_", &edp_.beta);                  // adaptive red param
00121     bind("interval_", &edp_.interval);      // adaptive red param
00122     bind("feng_adaptive_",&edp_.feng_adaptive); // adaptive red variant
00123     bind("targetdelay_", &edp_.targetdelay);    // target delay
00124     bind("top_", &edp_.top);            // maximum for max_p    
00125     bind("bottom_", &edp_.bottom);          // minimum for max_p    
00126     bind_bool("wait_", &edp_.wait);
00127     bind("linterm_", &edp_.max_p_inv);
00128     bind("mark_p_", &edp_.mark_p);
00129     bind_bool("setbit_", &edp_.setbit);     // mark instead of drop
00130     bind_bool("gentle_", &edp_.gentle);         // increase the packet
00131                             // drop prob. slowly
00132                             // when ave queue
00133                             // exceeds maxthresh
00134 
00135     bind_bool("summarystats_", &summarystats_);
00136     bind_bool("drop_tail_", &drop_tail_);       // drop last pkt
00137     //  _RENAMED("drop-tail_", "drop_tail_");
00138 
00139     bind_bool("drop_front_", &drop_front_);     // drop first pkt
00140     //  _RENAMED("drop-front_", "drop_front_");
00141     
00142     bind_bool("drop_rand_", &drop_rand_);       // drop pkt at random
00143     //  _RENAMED("drop-rand_", "drop_rand_");
00144 
00145     bind_bool("ns1_compat_", &ns1_compat_);     // ns-1 compatibility
00146     //  _RENAMED("ns1-compat_", "ns1_compat_");
00147 
00148     bind("ave_", &edv_.v_ave);          // average queue sie
00149     bind("prob1_", &edv_.v_prob1);          // dropping probability
00150     bind("curq_", &curq_);              // current queue size
00151     bind("cur_max_p_", &edv_.cur_max_p);        // current max_p
00152     
00153 
00154     q_ = new PacketQueue();             // underlying queue
00155     pq_ = q_;
00156     //reset();
00157 #ifdef notdef
00158     print_edp();
00159     print_edv();
00160 #endif
00161     
00162 }
00163 
00164 
00165 /*
00166  * Note: if the link bandwidth changes in the course of the
00167  * simulation, the bandwidth-dependent RED parameters do not change.
00168  * This should be fixed, but it would require some extra parameters,
00169  * and didn't seem worth the trouble...
00170  */
00171 void REDQueue::initialize_params()
00172 {
00173 /*
00174  * If q_weight=0, set it to a reasonable value of 1-exp(-1/C)
00175  * This corresponds to choosing q_weight to be of that value for
00176  * which the packet time constant -1/ln(1-q_weight) per default RTT 
00177  * of 100ms is an order of magnitude more than the link capacity, C.
00178  *
00179  * If q_weight=-1, then the queue weight is set to be a function of
00180  * the bandwidth and the link propagation delay.  In particular, 
00181  * the default RTT is assumed to be three times the link delay and 
00182  * transmission delay, if this gives a default RTT greater than 100 ms. 
00183  *
00184  * If q_weight=-2, set it to a reasonable value of 1-exp(-10/C).
00185  */
00186     if (edp_.q_w == 0.0) {
00187         edp_.q_w = 1.0 - exp(-1.0/edp_.ptc);
00188     } else if (edp_.q_w == -1.0) {
00189         double rtt = 3.0*(edp_.delay+1.0/edp_.ptc);
00190         //printf("delay: %5.4f rtt: %5.4f\n", edp_.delay, rtt);
00191         if (rtt < 0.1) 
00192             rtt = 0.1;
00193         edp_.q_w = 1.0 - exp(-1.0/(10*rtt*edp_.ptc));
00194     } else if (edp_.q_w == -2.0) {
00195         edp_.q_w = 1.0 - exp(-10.0/edp_.ptc);
00196     }
00197 
00198     // printf("ptc: %7.5f bandwidth: %5.3f pktsize: %d\n", edp_.ptc, link_->bandwidth(), edp_.mean_pktsize);
00199         // printf("th_min_pkts: %7.5f th_max_pkts: %7.5f\n", edp_.th_min_pkts, edp_.th_max);
00200     if (edp_.th_min_pkts == 0) {
00201         edp_.th_min_pkts = 5.0;
00202         // set th_min_pkts to half of targetqueue, if this is greater
00203         //  than 5 packets.
00204         double targetqueue = edp_.targetdelay * edp_.ptc;
00205         if (edp_.th_min_pkts < targetqueue / 2.0 )
00206             edp_.th_min_pkts = targetqueue / 2.0 ;
00207         }
00208     if (edp_.th_max_pkts == 0) 
00209         edp_.th_max_pkts = 3.0 * edp_.th_min_pkts;
00210         //printf("th_min_pkts: %7.5f th_max_pkts: %7.5f\n", edp_.th_min_pkts, edp_.th_max);
00211     //printf("q_w: %7.5f\n", edp_.q_w);
00212     if (edp_.bottom == 0) {
00213         edp_.bottom = 0.01;
00214         // Set bottom to at most 1/W, for W the delay-bandwidth 
00215         //   product in packets for a connection with this bandwidth,
00216         //   1000-byte packets, and 100 ms RTTs.
00217         // So W = 0.1 * link_->bandwidth() / 8000 
00218         double bottom1 = 80000.0/link_->bandwidth();
00219         if (bottom1 < edp_.bottom) 
00220             edp_.bottom = bottom1;
00221         //printf("bottom: %9.7f\n", edp_.bottom);
00222     }
00223 }
00224 
00225 void REDQueue::initParams() 
00226 {
00227     edp_.mean_pktsize = 0;
00228     edp_.idle_pktsize = 0;
00229     edp_.bytes = 0;
00230     edp_.wait = 0;
00231     edp_.setbit = 0;
00232     edp_.gentle = 0;
00233     edp_.th_min = 0.0;
00234     edp_.th_min_pkts = 0.0;
00235     edp_.th_max = 0.0;
00236     edp_.th_max_pkts = 0.0;
00237     edp_.max_p_inv = 0.0;
00238     edp_.mark_p = 0.0;
00239     edp_.q_w = 0.0;
00240     edp_.adaptive = 0;
00241     edp_.cautious = 0;
00242     edp_.alpha = 0.0;
00243     edp_.beta = 0.0;
00244     edp_.interval = 0.0;
00245     edp_.targetdelay = 0.0;
00246     edp_.top = 0.0;
00247     edp_.bottom = 0.0;
00248     edp_.feng_adaptive = 0;
00249     edp_.ptc = 0.0;
00250     edp_.delay = 0.0;
00251     
00252     edv_.v_ave = 0.0;
00253     edv_.v_prob1 = 0.0;
00254     edv_.v_slope = 0.0;
00255     edv_.v_prob = 0.0;
00256     edv_.v_a = 0.0;
00257     edv_.v_b = 0.0;
00258     edv_.v_c = 0.0;
00259     edv_.v_d = 0.0;
00260     edv_.count = 0;
00261     edv_.count_bytes = 0;
00262     edv_.old = 0;
00263     edv_.cur_max_p = 1.0;
00264     edv_.lastset = 0;
00265 }
00266 
00267 
00268 void REDQueue::reset()
00269 {
00270     
00271         //printf("3: th_min_pkts: %5.2f\n", edp_.th_min_pkts); 
00272     /*
00273      * Compute the "packet time constant" if we know the
00274      * link bandwidth.  The ptc is the max number of (avg sized)
00275      * pkts per second which can be placed on the link.
00276      * The link bw is given in bits/sec, so scale mean psize
00277      * accordingly.
00278      */
00279         if (link_) {
00280         edp_.ptc = link_->bandwidth() / (8. * edp_.mean_pktsize);
00281         initialize_params();
00282     }
00283     if (edp_.th_max_pkts == 0) 
00284         edp_.th_max_pkts = 3.0 * edp_.th_min_pkts;
00285     /*
00286      * If queue is measured in bytes, scale min/max thresh
00287      * by the size of an average packet (which is specified by user).
00288      */
00289         if (qib_) {
00290         //printf("1: th_min in pkts: %5.2f mean_pktsize: %d \n", edp_.th_min_pkts, edp_.mean_pktsize); 
00291                 edp_.th_min = edp_.th_min_pkts * edp_.mean_pktsize;  
00292                 edp_.th_max = edp_.th_max_pkts * edp_.mean_pktsize;
00293         //printf("2: th_min in bytes (if qib): %5.2f mean_pktsize: %d \n", edp_.th_min, edp_.mean_pktsize); 
00294         } else {
00295         edp_.th_min = edp_.th_min_pkts;
00296         edp_.th_max = edp_.th_max_pkts;
00297     }
00298      
00299     edv_.v_ave = 0.0;
00300     edv_.v_slope = 0.0;
00301     edv_.count = 0;
00302     edv_.count_bytes = 0;
00303     edv_.old = 0;
00304     double th_diff = (edp_.th_max - edp_.th_min);
00305     if (th_diff == 0) { 
00306         //XXX this last check was added by a person who knows
00307         //nothing of this code just to stop FP div by zero.
00308         //Values for thresholds were equal at time 0.  If you
00309         //know what should be here, please cleanup and remove
00310         //this comment.
00311         th_diff = 1.0; 
00312     }
00313     edv_.v_a = 1.0 / th_diff;
00314     edv_.cur_max_p = 1.0 / edp_.max_p_inv;
00315     edv_.v_b = - edp_.th_min / th_diff;
00316     edv_.lastset = 0.0;
00317     if (edp_.gentle) {
00318         edv_.v_c = ( 1.0 - edv_.cur_max_p ) / edp_.th_max;
00319         edv_.v_d = 2 * edv_.cur_max_p - 1.0;
00320     }
00321 
00322     idle_ = 1;
00323     if (&Scheduler::instance() != NULL)
00324         idletime_ = Scheduler::instance().clock();
00325     else
00326         idletime_ = 0.0; /* sched not instantiated yet */
00327     
00328     if (debug_) 
00329         printf("Doing a queue reset\n");
00330     Queue::reset();
00331     if (debug_) 
00332         printf("Done queue reset\n");
00333 }
00334 
00335 /*
00336  *  Updating max_p, following code from Feng et al. 
00337  *  This is only called for Adaptive RED.
00338  *  From "A Self-Configuring RED Gateway", from Feng et al.
00339  *  They recommend alpha = 3, and beta = 2.
00340  */
00341 void REDQueue::updateMaxPFeng(double new_ave)
00342 {
00343     if ( edp_.th_min < new_ave && new_ave < edp_.th_max) {
00344         edv_.status = edv_.Between;
00345     }
00346     if (new_ave < edp_.th_min && edv_.status != edv_.Below) {
00347         edv_.status = edv_.Below;
00348         edv_.cur_max_p = edv_.cur_max_p / edp_.alpha;
00349         //double max = edv_.cur_max_p; double param = edp_.alpha;
00350         //printf("max: %5.2f alpha: %5.2f\n", max, param);
00351     }
00352     if (new_ave > edp_.th_max && edv_.status != edv_.Above) {
00353         edv_.status = edv_.Above;
00354         edv_.cur_max_p = edv_.cur_max_p * edp_.beta;
00355         //double max = edv_.cur_max_p; double param = edp_.alpha;
00356         //printf("max: %5.2f beta: %5.2f\n", max, param);
00357     }
00358 }
00359 
00360 /*
00361  *  Updating max_p to keep the average queue size within the target range.
00362  *  This is only called for Adaptive RED.
00363  */
00364 void REDQueue::updateMaxP(double new_ave, double now)
00365 {
00366     double part = 0.4*(edp_.th_max - edp_.th_min);
00367     // AIMD rule to keep target Q~1/2(th_min+th_max)
00368     if ( new_ave < edp_.th_min + part && edv_.cur_max_p > edp_.bottom) {
00369         // we increase the average queue size, so decrease max_p
00370         edv_.cur_max_p = edv_.cur_max_p * edp_.beta;
00371         edv_.lastset = now;
00372     } else if (new_ave > edp_.th_max - part && edp_.top > edv_.cur_max_p ) {
00373         // we decrease the average queue size, so increase max_p
00374         double alpha = edp_.alpha;
00375                         if ( alpha > 0.25*edv_.cur_max_p )
00376             alpha = 0.25*edv_.cur_max_p;
00377         edv_.cur_max_p = edv_.cur_max_p + alpha;
00378         edv_.lastset = now;
00379     } 
00380 }
00381 
00382 /*
00383  * Compute the average queue size.
00384  * Nqueued can be bytes or packets.
00385  */
00386 double REDQueue::estimator(int nqueued, int m, double ave, double q_w)
00387 {
00388     double new_ave, old_ave;
00389 
00390     new_ave = ave;
00391     while (--m >= 1) {
00392         new_ave *= 1.0 - q_w;
00393     }
00394     old_ave = new_ave;
00395     new_ave *= 1.0 - q_w;
00396     new_ave += q_w * nqueued;
00397     
00398     double now = Scheduler::instance().clock();
00399     if (edp_.adaptive == 1) {
00400         if (edp_.feng_adaptive == 1)
00401             updateMaxPFeng(new_ave);
00402         else if (now > edv_.lastset + edp_.interval)
00403             updateMaxP(new_ave, now);
00404     }
00405     return new_ave;
00406 }
00407 
00408 /*
00409  * Return the next packet in the queue for transmission.
00410  */
00411 Packet* REDQueue::deque()
00412 {
00413     Packet *p;
00414     if (summarystats_ && &Scheduler::instance() != NULL) {
00415         Queue::updateStats(qib_?q_->byteLength():q_->length());
00416     }
00417     p = q_->deque();
00418     if (p != 0) {
00419         idle_ = 0;
00420     } else {
00421         idle_ = 1;
00422         // deque() may invoked by Queue::reset at init
00423         // time (before the scheduler is instantiated).
00424         // deal with this case
00425         if (&Scheduler::instance() != NULL)
00426             idletime_ = Scheduler::instance().clock();
00427         else
00428             idletime_ = 0.0;
00429     }
00430     return (p);
00431 }
00432 
00433 /*
00434  * Calculate the drop probability.
00435  */
00436 double
00437 REDQueue::calculate_p_new(double v_ave, double th_max, int gentle, double v_a, 
00438     double v_b, double v_c, double v_d, double max_p)
00439 {
00440     double p;
00441     if (gentle && v_ave >= th_max) {
00442         // p ranges from max_p to 1 as the average queue
00443         // size ranges from th_max to twice th_max 
00444         p = v_c * v_ave + v_d;
00445     } else {
00446         // p ranges from 0 to max_p as the average queue
00447         // size ranges from th_min to th_max 
00448         p = v_a * v_ave + v_b;
00449         p *= max_p;
00450     }
00451     if (p > 1.0)
00452         p = 1.0;
00453     return p;
00454 }
00455 
00456 /*
00457  * Calculate the drop probability.
00458  * This is being kept for backwards compatibility.
00459  */
00460 double
00461 REDQueue::calculate_p(double v_ave, double th_max, int gentle, double v_a, 
00462     double v_b, double v_c, double v_d, double max_p_inv)
00463 {
00464     double p = calculate_p_new(v_ave, th_max, gentle, v_a,
00465         v_b, v_c, v_d, 1.0 / max_p_inv);
00466     return p;
00467 }
00468 
00469 /*
00470  * Make uniform instead of geometric interdrop periods.
00471  */
00472 double
00473 REDQueue::modify_p(double p, int count, int count_bytes, int bytes, 
00474    int mean_pktsize, int wait, int size)
00475 {
00476     double count1 = (double) count;
00477     if (bytes)
00478         count1 = (double) (count_bytes/mean_pktsize);
00479     if (wait) {
00480         if (count1 * p < 1.0)
00481             p = 0.0;
00482         else if (count1 * p < 2.0)
00483             p /= (2 - count1 * p);
00484         else
00485             p = 1.0;
00486     } else {
00487         if (count1 * p < 1.0)
00488             p /= (1.0 - count1 * p);
00489         else
00490             p = 1.0;
00491     }
00492     if (bytes && p < 1.0) {
00493         p = p * size / mean_pktsize;
00494     }
00495     if (p > 1.0)
00496         p = 1.0;
00497     return p;
00498 }
00499 
00500 /*
00501  * 
00502  */
00503 
00504 /*
00505  * should the packet be dropped/marked due to a probabilistic drop?
00506  */
00507 int
00508 REDQueue::drop_early(Packet* pkt)
00509 {
00510     hdr_cmn* ch = hdr_cmn::access(pkt);
00511 
00512     edv_.v_prob1 = calculate_p_new(edv_.v_ave, edp_.th_max, edp_.gentle, 
00513       edv_.v_a, edv_.v_b, edv_.v_c, edv_.v_d, edv_.cur_max_p);
00514     edv_.v_prob = modify_p(edv_.v_prob1, edv_.count, edv_.count_bytes,
00515       edp_.bytes, edp_.mean_pktsize, edp_.wait, ch->size());
00516 
00517     // drop probability is computed, pick random number and act
00518     if (edp_.cautious == 1) {
00519          // Don't drop/mark if the instantaneous queue is much
00520          //  below the average.
00521          // For experimental purposes only.
00522         int qsize = qib_?q_->byteLength():q_->length();
00523         // pkts: the number of packets arriving in 50 ms
00524         double pkts = edp_.ptc * 0.05;
00525         double fraction = pow( (1-edp_.q_w), pkts);
00526         // double fraction = 0.9;
00527         if ((double) qsize < fraction * edv_.v_ave) {
00528             // queue could have been empty for 0.05 seconds
00529             // printf("fraction: %5.2f\n", fraction);
00530             return (0);
00531         }
00532     }
00533     double u = Random::uniform();
00534     if (edp_.cautious == 2) {
00535                 // Decrease the drop probability if the instantaneous
00536         //   queue is much below the average.
00537         // For experimental purposes only.
00538         int qsize = qib_?q_->byteLength():q_->length();
00539         // pkts: the number of packets arriving in 50 ms
00540         double pkts = edp_.ptc * 0.05;
00541         double fraction = pow( (1-edp_.q_w), pkts);
00542         // double fraction = 0.9;
00543         double ratio = qsize / (fraction * edv_.v_ave);
00544         if (ratio < 1.0) {
00545             // printf("ratio: %5.2f\n", ratio);
00546             u *= 1.0 / ratio;
00547         }
00548     }
00549     if (u <= edv_.v_prob) {
00550         // DROP or MARK
00551         edv_.count = 0;
00552         edv_.count_bytes = 0;
00553         hdr_flags* hf = hdr_flags::access(pickPacketForECN(pkt));
00554         if (edp_.setbit && hf->ect() && edv_.v_prob1 < edp_.mark_p) { 
00555             hf->ce() = 1;   // mark Congestion Experienced bit
00556             // Tell the queue monitor here - call emark(pkt)
00557             return (0); // no drop
00558         } else {
00559             return (1); // drop
00560         }
00561     }
00562     return (0);         // no DROP/mark
00563 }
00564 
00565 /*
00566  * Pick packet for early congestion notification (ECN). This packet is then
00567  * marked or dropped. Having a separate function do this is convenient for
00568  * supporting derived classes that use the standard RED algorithm to compute
00569  * average queue size but use a different algorithm for choosing the packet for 
00570  * ECN notification.
00571  */
00572 Packet*
00573 REDQueue::pickPacketForECN(Packet* pkt)
00574 {
00575     return pkt; /* pick the packet that just arrived */
00576 }
00577 
00578 /*
00579  * Pick packet to drop. Having a separate function do this is convenient for
00580  * supporting derived classes that use the standard RED algorithm to compute
00581  * average queue size but use a different algorithm for choosing the victim.
00582  */
00583 Packet*
00584 REDQueue::pickPacketToDrop() 
00585 {
00586     int victim;
00587 
00588     if (drop_front_)
00589         victim = min(1, q_->length()-1);
00590     else if (drop_rand_)
00591         victim = Random::integer(q_->length());
00592     else            /* default is drop_tail_ */
00593         victim = q_->length() - 1;
00594 
00595     return(q_->lookup(victim)); 
00596 }
00597 
00598 /*
00599  * Receive a new packet arriving at the queue.
00600  * The average queue size is computed.  If the average size
00601  * exceeds the threshold, then the dropping probability is computed,
00602  * and the newly-arriving packet is dropped with that probability.
00603  * The packet is also dropped if the maximum queue size is exceeded.
00604  *
00605  * "Forced" drops mean a packet arrived when the underlying queue was
00606  * full, or when the average queue size exceeded some threshold and no
00607  * randomization was used in selecting the packet to be dropped.
00608  * "Unforced" means a RED random drop.
00609  *
00610  * For forced drops, either the arriving packet is dropped or one in the
00611  * queue is dropped, depending on the setting of drop_tail_.
00612  * For unforced drops, the arriving packet is always the victim.
00613  */
00614 
00615 #define DTYPE_NONE  0   /* ok, no drop */
00616 #define DTYPE_FORCED    1   /* a "forced" drop */
00617 #define DTYPE_UNFORCED  2   /* an "unforced" (random) drop */
00618 
00619 void REDQueue::enque(Packet* pkt)
00620 {
00621 
00622     /*
00623      * if we were idle, we pretend that m packets arrived during
00624      * the idle period.  m is set to be the ptc times the amount
00625      * of time we've been idle for
00626      */
00627 
00628     int m = 0;
00629     if (idle_) {
00630         // A packet that arrives to an idle queue will never
00631         //  be dropped.
00632         double now = Scheduler::instance().clock();
00633         /* To account for the period when the queue was empty. */
00634         idle_ = 0;
00635         // Use idle_pktsize instead of mean_pktsize, for
00636         //  a faster response to idle times.
00637         if (edp_.cautious == 3) {
00638             double ptc = edp_.ptc * 
00639                edp_.mean_pktsize / edp_.idle_pktsize;
00640             m = int(ptc * (now - idletime_));
00641         } else
00642                     m = int(edp_.ptc * (now - idletime_));
00643     }
00644 
00645     /*
00646      * Run the estimator with either 1 new packet arrival, or with
00647      * the scaled version above [scaled by m due to idle time]
00648      */
00649     edv_.v_ave = estimator(qib_ ? q_->byteLength() : q_->length(), m + 1, edv_.v_ave, edp_.q_w);
00650     //printf("v_ave: %6.4f (%13.12f) q: %d)\n", 
00651     //  double(edv_.v_ave), double(edv_.v_ave), q_->length());
00652     if (summarystats_) {
00653         /* compute true average queue size for summary stats */
00654         Queue::updateStats(qib_?q_->byteLength():q_->length());
00655     }
00656 
00657     /*
00658      * count and count_bytes keeps a tally of arriving traffic
00659      * that has not been dropped (i.e. how long, in terms of traffic,
00660      * it has been since the last early drop)
00661      */
00662 
00663     hdr_cmn* ch = hdr_cmn::access(pkt);
00664     ++edv_.count;
00665     edv_.count_bytes += ch->size();
00666 
00667     /*
00668      * DROP LOGIC:
00669      *  q = current q size, ~q = averaged q size
00670      *  1> if ~q > maxthresh, this is a FORCED drop
00671      *  2> if minthresh < ~q < maxthresh, this may be an UNFORCED drop
00672      *  3> if (q+1) > hard q limit, this is a FORCED drop
00673      */
00674 
00675     register double qavg = edv_.v_ave;
00676     int droptype = DTYPE_NONE;
00677     int qlen = qib_ ? q_->byteLength() : q_->length();
00678     int qlim = qib_ ? (qlim_ * edp_.mean_pktsize) : qlim_;
00679 
00680     curq_ = qlen;   // helps to trace queue during arrival, if enabled
00681 
00682     if (qavg >= edp_.th_min && qlen > 1) {
00683         if ((!edp_.gentle && qavg >= edp_.th_max) ||
00684             (edp_.gentle && qavg >= 2 * edp_.th_max)) {
00685             droptype = DTYPE_FORCED;
00686         } else if (edv_.old == 0) {
00687             /* 
00688              * The average queue size has just crossed the
00689              * threshold from below to above "minthresh", or
00690              * from above "minthresh" with an empty queue to
00691              * above "minthresh" with a nonempty queue.
00692              */
00693             edv_.count = 1;
00694             edv_.count_bytes = ch->size();
00695             edv_.old = 1;
00696         } else if (drop_early(pkt)) {
00697             droptype = DTYPE_UNFORCED;
00698         }
00699     } else {
00700         /* No packets are being dropped.  */
00701         edv_.v_prob = 0.0;
00702         edv_.old = 0;       
00703     }
00704     if (qlen >= qlim) {
00705         // see if we've exceeded the queue size
00706         droptype = DTYPE_FORCED;
00707     }
00708 
00709     if (droptype == DTYPE_UNFORCED) {
00710         /* pick packet for ECN, which is dropping in this case */
00711         Packet *pkt_to_drop = pickPacketForECN(pkt);
00712         /* 
00713          * If the packet picked is different that the one that just arrived,
00714          * add it to the queue and remove the chosen packet.
00715          */
00716         if (pkt_to_drop != pkt) {
00717             q_->enque(pkt);
00718             q_->remove(pkt_to_drop);
00719             pkt = pkt_to_drop; /* XXX okay because pkt is not needed anymore */
00720         }
00721 
00722         // deliver to special "edrop" target, if defined
00723         if (de_drop_ != NULL) {
00724     
00725         //trace first if asked 
00726         // if no snoop object (de_drop_) is defined, 
00727         // this packet will not be traced as a special case.
00728             if (EDTrace != NULL) 
00729                 ((Trace *)EDTrace)->recvOnly(pkt);
00730 
00731             reportDrop(pkt);
00732             de_drop_->recv(pkt);
00733         }
00734         else {
00735             reportDrop(pkt);
00736             drop(pkt);
00737         }
00738     } else {
00739         /* forced drop, or not a drop: first enqueue pkt */
00740         q_->enque(pkt);
00741 
00742         /* drop a packet if we were told to */
00743         if (droptype == DTYPE_FORCED) {
00744             /* drop random victim or last one */
00745             pkt = pickPacketToDrop();
00746             q_->remove(pkt);
00747             reportDrop(pkt);
00748             drop(pkt);
00749             if (!ns1_compat_) {
00750                 // bug-fix from Philip Liu, <phill@ece.ubc.ca>
00751                 edv_.count = 0;
00752                 edv_.count_bytes = 0;
00753             }
00754         }
00755     }
00756     return;
00757 }
00758 
00759 int REDQueue::command(int argc, const char*const* argv)
00760 {
00761     Tcl& tcl = Tcl::instance();
00762     if (argc == 2) {
00763         if (strcmp(argv[1], "reset") == 0) {
00764             reset();
00765             return (TCL_OK);
00766         }
00767         if (strcmp(argv[1], "early-drop-target") == 0) {
00768             if (de_drop_ != NULL)
00769                 tcl.resultf("%s", de_drop_->name());
00770             return (TCL_OK);
00771         }
00772         if (strcmp(argv[1], "edrop-trace") == 0) {
00773             if (EDTrace != NULL) {
00774                 tcl.resultf("%s", EDTrace->name());
00775                 if (debug_) 
00776                     printf("edrop trace exists according to RED\n");
00777             }
00778             else {
00779                 if (debug_)
00780                     printf("edrop trace doesn't exist according to RED\n");
00781                 tcl.resultf("0");
00782             }
00783             return (TCL_OK);
00784         }
00785         if (strcmp(argv[1], "trace-type") == 0) {
00786             tcl.resultf("%s", traceType);
00787             return (TCL_OK);
00788         }
00789         if (strcmp(argv[1], "printstats") == 0) {
00790             print_summarystats();
00791             return (TCL_OK);
00792         }
00793     } 
00794     else if (argc == 3) {
00795         // attach a file for variable tracing
00796         if (strcmp(argv[1], "attach") == 0) {
00797             int mode;
00798             const char* id = argv[2];
00799             tchan_ = Tcl_GetChannel(tcl.interp(), (char*)id, &mode);
00800             if (tchan_ == 0) {
00801                 tcl.resultf("RED: trace: can't attach %s for writing", id);
00802                 return (TCL_ERROR);
00803             }
00804             return (TCL_OK);
00805         }
00806         // tell RED about link stats
00807         if (strcmp(argv[1], "link") == 0) {
00808             LinkDelay* del = (LinkDelay*)TclObject::lookup(argv[2]);
00809             if (del == 0) {
00810                 tcl.resultf("RED: no LinkDelay object %s",
00811                     argv[2]);
00812                 return(TCL_ERROR);
00813             }
00814             // set ptc now
00815             link_ = del;
00816             edp_.ptc = link_->bandwidth() /
00817                 (8. * edp_.mean_pktsize);
00818             edp_.delay = link_->delay();
00819             if (
00820               (edp_.q_w <= 0.0 || edp_.th_min_pkts == 0 ||
00821                     edp_.th_max_pkts == 0))
00822                             initialize_params();
00823             return (TCL_OK);
00824         }
00825         if (strcmp(argv[1], "early-drop-target") == 0) {
00826             NsObject* p = (NsObject*)TclObject::lookup(argv[2]);
00827             if (p == 0) {
00828                 tcl.resultf("no object %s", argv[2]);
00829                 return (TCL_ERROR);
00830             }
00831             de_drop_ = p;
00832             return (TCL_OK);
00833         }
00834         if (strcmp(argv[1], "edrop-trace") == 0) {
00835             if (debug_) 
00836                 printf("Ok, Here\n");
00837             NsObject * t  = (NsObject *)TclObject::lookup(argv[2]);
00838             if (debug_)  
00839                 printf("Ok, Here too\n");
00840             if (t == 0) {
00841                 tcl.resultf("no object %s", argv[2]);
00842                 return (TCL_ERROR);
00843             }
00844             EDTrace = t;
00845             if (debug_)  
00846                 printf("Ok, Here too too too %d\n", ((Trace *)EDTrace)->type_);
00847             return (TCL_OK);
00848         }
00849         if (!strcmp(argv[1], "packetqueue-attach")) {
00850             delete q_;
00851             if (!(q_ = (PacketQueue*) TclObject::lookup(argv[2])))
00852                 return (TCL_ERROR);
00853             else {
00854                 pq_ = q_;
00855                 return (TCL_OK);
00856             }
00857         }
00858     }
00859     return (Queue::command(argc, argv));
00860 }
00861 
00862 /*
00863  * Routine called by TracedVar facility when variables change values.
00864  * Currently used to trace values of avg queue size, drop probability,
00865  * and the instantaneous queue size seen by arriving packets.
00866  * Note that the tracing of each var must be enabled in tcl to work.
00867  */
00868 
00869 void
00870 REDQueue::trace(TracedVar* v)
00871 {
00872     char wrk[500], *p;
00873 
00874     if (((p = strstr(v->name(), "ave")) == NULL) &&
00875         ((p = strstr(v->name(), "prob")) == NULL) &&
00876         ((p = strstr(v->name(), "curq")) == NULL) &&
00877         ((p = strstr(v->name(), "cur_max_p"))==NULL) ) {
00878         fprintf(stderr, "RED:unknown trace var %s\n",
00879             v->name());
00880         return;
00881     }
00882 
00883     if (tchan_) {
00884         int n;
00885         double t = Scheduler::instance().clock();
00886         // XXX: be compatible with nsv1 RED trace entries
00887         if (strstr(v->name(), "curq") != NULL) {
00888             sprintf(wrk, "Q %g %d", t, int(*((TracedInt*) v)));
00889         } else {
00890             sprintf(wrk, "%c %g %g", *p, t,
00891                 double(*((TracedDouble*) v)));
00892         }
00893         n = strlen(wrk);
00894         wrk[n] = '\n'; 
00895         wrk[n+1] = 0;
00896         (void)Tcl_Write(tchan_, wrk, n+1);
00897     }
00898     return; 
00899 }
00900 
00901 /* for debugging help */
00902 void REDQueue::print_edp()
00903 {
00904     printf("mean_pktsz: %d\n", edp_.mean_pktsize); 
00905     printf("bytes: %d, wait: %d, setbit: %d\n",
00906         edp_.bytes, edp_.wait, edp_.setbit);
00907     printf("minth: %f, maxth: %f\n", edp_.th_min, edp_.th_max);
00908     printf("max_p: %f, qw: %f, ptc: %f\n",
00909         (double) edv_.cur_max_p, edp_.q_w, edp_.ptc);
00910     printf("qlim: %d, idletime: %f\n", qlim_, idletime_);
00911     printf("=========\n");
00912 }
00913 
00914 void REDQueue::print_edv()
00915 {
00916     printf("v_a: %f, v_b: %f\n", edv_.v_a, edv_.v_b);
00917 }
00918 
00919 void REDQueue::print_summarystats()
00920 {
00921     //double now = Scheduler::instance().clock();
00922     printf("True average queue: %5.3f", true_ave_);
00923     if (qib_) 
00924         printf(" (in bytes)");
00925         printf(" time: %5.3f\n", total_time_);
00926 }
00927 
00928 /************************************************************/
00929 /*
00930  * This procedure is obsolete, and only included for backward compatibility.
00931  * The new procedure is REDQueue::estimator
00932  */ 
00933 /*
00934  * Compute the average queue size.
00935  * The code contains two alternate methods for this, the plain EWMA
00936  * and the Holt-Winters method.
00937  * nqueued can be bytes or packets
00938  */
00939 void REDQueue::run_estimator(int nqueued, int m)
00940 {
00941     double f, f_sl, f_old;
00942 
00943     f = edv_.v_ave;
00944     f_sl = edv_.v_slope;
00945 #define RED_EWMA
00946 #ifdef RED_EWMA
00947     while (--m >= 1) {
00948         f_old = f;
00949         f *= 1.0 - edp_.q_w;
00950     }
00951     f_old = f;
00952     f *= 1.0 - edp_.q_w;
00953     f += edp_.q_w * nqueued;
00954 #endif
00955 #ifdef RED_HOLT_WINTERS
00956     while (--m >= 1) {
00957         f_old = f;
00958         f += f_sl;
00959         f *= 1.0 - edp_.q_w;
00960         f_sl *= 1.0 - 0.5 * edp_.q_w;
00961         f_sl += 0.5 * edp_.q_w * (f - f_old);
00962     }
00963     f_old = f;
00964     f += f_sl;
00965     f *= 1.0 - edp_.q_w;
00966     f += edp_.q_w * nqueued;
00967     f_sl *= 1.0 - 0.5 * edp_.q_w;
00968     f_sl += 0.5 * edp_.q_w * (f - f_old);
00969 #endif
00970     edv_.v_ave = f;
00971     edv_.v_slope = f_sl;
00972 }
00973 

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