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

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

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