tcp-vegas.cc

Go to the documentation of this file.
00001 /* -*-  Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
00002 
00003 /*
00004  * tcp-vegas.cc
00005  * Copyright (C) 1997 by the University of Southern California
00006  * $Id: tcp-vegas.cc,v 1.37 2005/08/25 18:58:12 johnh Exp $
00007  *
00008  * This program is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU General Public License,
00010  * version 2, as published by the Free Software Foundation.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License along
00018  * with this program; if not, write to the Free Software Foundation, Inc.,
00019  * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
00020  *
00021  *
00022  * The copyright of this module includes the following
00023  * linking-with-specific-other-licenses addition:
00024  *
00025  * In addition, as a special exception, the copyright holders of
00026  * this module give you permission to combine (via static or
00027  * dynamic linking) this module with free software programs or
00028  * libraries that are released under the GNU LGPL and with code
00029  * included in the standard release of ns-2 under the Apache 2.0
00030  * license or under otherwise-compatible licenses with advertising
00031  * requirements (or modified versions of such code, with unchanged
00032  * license).  You may copy and distribute such a system following the
00033  * terms of the GNU GPL for this module and the licenses of the
00034  * other code concerned, provided that you include the source code of
00035  * that other code when and as the GNU GPL requires distribution of
00036  * source code.
00037  *
00038  * Note that people who make modified versions of this module
00039  * are not obligated to grant this special exception for their
00040  * modified versions; it is their choice whether to do so.  The GNU
00041  * General Public License gives permission to release a modified
00042  * version without this exception; this exception also makes it
00043  * possible to release a modified version which carries forward this
00044  * exception.
00045  *
00046  */
00047 
00048 /*
00049  * ns-1 implementation:
00050  *
00051  * This is an implementation of U. of Arizona's TCP Vegas. I implemented
00052  * it based on USC's NetBSD-Vegas.
00053  *                  Ted Kuo
00054  *                  North Carolina St. Univ. and
00055  *                  Networking Software Div, IBM
00056  *                  tkuo@eos.ncsu.edu
00057  */
00058 
00059 #ifndef lint
00060 static const char rcsid[] =
00061 "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/tcp/tcp-vegas.cc,v 1.37 2005/08/25 18:58:12 johnh Exp $ (NCSU/IBM)";
00062 #endif
00063 
00064 #include <stdio.h>
00065 #include <stdlib.h>
00066 #include <sys/types.h>
00067 
00068 #include "ip.h"
00069 #include "tcp.h"
00070 #include "flags.h"
00071 
00072 #define MIN(x, y) ((x)<(y) ? (x) : (y))
00073 
00074 
00075 static class VegasTcpClass : public TclClass {
00076 public:
00077     VegasTcpClass() : TclClass("Agent/TCP/Vegas") {}
00078     TclObject* create(int, const char*const*) {
00079         return (new VegasTcpAgent());
00080     }
00081 } class_vegas;
00082 
00083 
00084 VegasTcpAgent::VegasTcpAgent() : TcpAgent()
00085 {
00086     v_sendtime_ = NULL;
00087     v_transmits_ = NULL;
00088 }
00089 
00090 VegasTcpAgent::~VegasTcpAgent()
00091 {
00092     if (v_sendtime_)
00093         delete []v_sendtime_;
00094     if (v_transmits_)
00095         delete []v_transmits_;
00096 }
00097 
00098 void
00099 VegasTcpAgent::delay_bind_init_all()
00100 {
00101     delay_bind_init_one("v_alpha_");
00102     delay_bind_init_one("v_beta_");
00103     delay_bind_init_one("v_gamma_");
00104     delay_bind_init_one("v_rtt_");
00105     TcpAgent::delay_bind_init_all();
00106         reset();
00107 }
00108 
00109 int
00110 VegasTcpAgent::delay_bind_dispatch(const char *varName, const char *localName, 
00111                    TclObject *tracer)
00112 {
00113     /* init vegas var */
00114         if (delay_bind(varName, localName, "v_alpha_", &v_alpha_, tracer)) 
00115         return TCL_OK;
00116         if (delay_bind(varName, localName, "v_beta_", &v_beta_, tracer)) 
00117         return TCL_OK;
00118         if (delay_bind(varName, localName, "v_gamma_", &v_gamma_, tracer)) 
00119         return TCL_OK;
00120         if (delay_bind(varName, localName, "v_rtt_", &v_rtt_, tracer)) 
00121         return TCL_OK;
00122         return TcpAgent::delay_bind_dispatch(varName, localName, tracer);
00123 }
00124 
00125 void
00126 VegasTcpAgent::reset()
00127 {
00128     t_cwnd_changed_ = 0.;
00129     firstrecv_ = -1.0;
00130     v_slowstart_ = 2;
00131     v_sa_ = 0;
00132     v_sd_ = 0;
00133     v_timeout_ = 1000.;
00134     v_worried_ = 0;
00135     v_begseq_ = 0;
00136     v_begtime_ = 0.;
00137     v_cntRTT_ = 0; v_sumRTT_ = 0.;
00138     v_baseRTT_ = 1000000000.;
00139     v_incr_ = 0;
00140     v_inc_flag_ = 1;
00141 
00142     TcpAgent::reset();
00143 }
00144 
00145 void
00146 VegasTcpAgent::recv_newack_helper(Packet *pkt)
00147 {
00148     newack(pkt);
00149 #if 0
00150     // like TcpAgent::recv_newack_helper, but without this
00151     if ( !hdr_flags::access(pkt)->ecnecho() || !ecn_ ) {
00152             opencwnd();
00153     }
00154 #endif
00155     /* if the connection is done, call finish() */
00156     if ((highest_ack_ >= curseq_-1) && !closed_) {
00157         closed_ = 1;
00158         finish();
00159     }
00160 }
00161 
00162 void
00163 VegasTcpAgent::recv(Packet *pkt, Handler *)
00164 {
00165     double currentTime = vegastime();
00166     hdr_tcp *tcph = hdr_tcp::access(pkt);
00167     hdr_flags *flagh = hdr_flags::access(pkt);
00168 
00169 #if 0
00170     if (pkt->type_ != PT_ACK) {
00171         Tcl::instance().evalf("%s error \"recieved non-ack\"",
00172                       name());
00173         Packet::free(pkt);
00174         return;
00175     }
00176 #endif /* 0 */
00177     ++nackpack_;
00178 
00179     if(firstrecv_<0) { // init vegas rtt vars
00180         firstrecv_ = currentTime;
00181         v_baseRTT_ = v_rtt_ = firstrecv_;
00182         v_sa_  = v_rtt_ * 8.;
00183         v_sd_  = v_rtt_;
00184         v_timeout_ = ((v_sa_/4.)+v_sd_)/2.;
00185     }
00186 
00187     if (flagh->ecnecho())
00188         ecn(tcph->seqno());
00189     if (tcph->seqno() > last_ack_) {
00190         if (last_ack_ == 0 && delay_growth_) {
00191             cwnd_ = initial_window();
00192         }
00193         /* check if cwnd has been inflated */
00194         if(dupacks_ > numdupacks_ &&  cwnd_ > v_newcwnd_) {
00195             cwnd_ = v_newcwnd_;
00196             // vegas ssthresh is used only during slow-start
00197             ssthresh_ = 2;
00198         }
00199         int oldack = last_ack_;
00200 
00201         recv_newack_helper(pkt);
00202         
00203         /*
00204          * begin of once per-rtt actions
00205          *  1. update path fine-grained rtt and baseRTT
00206          *      2. decide what to do with cwnd_, inc/dec/unchanged
00207          *         based on delta=expect - actual.
00208          */
00209         if(tcph->seqno() >= v_begseq_) {
00210             double rtt;
00211             if(v_cntRTT_ > 0)
00212                 rtt = v_sumRTT_ / v_cntRTT_;
00213             else 
00214                 rtt = currentTime - v_begtime_;
00215 
00216             v_sumRTT_ = 0.0;
00217             v_cntRTT_ = 0;
00218 
00219             // calc # of packets in transit
00220             int rttLen = t_seqno_ - v_begseq_;
00221 
00222             /*
00223              * decide should we incr/decr cwnd_ by how much
00224              */
00225             if(rtt>0) {
00226                 /* if there's only one pkt in transit, update 
00227                  * baseRTT
00228                  */
00229                 if(rtt<v_baseRTT_ || rttLen<=1)
00230                     v_baseRTT_ = rtt;
00231 
00232                 double expect;   // in pkt/sec
00233                 // actual = (# in transit)/(current rtt) 
00234                 v_actual_ = double(rttLen)/rtt;
00235                 // expect = (current window size)/baseRTT
00236                 expect = double(t_seqno_-last_ack_)/v_baseRTT_;
00237 
00238                 // calc actual and expect thruput diff, delta
00239                 int delta=int((expect-v_actual_)*v_baseRTT_+0.5);
00240                 if(cwnd_ < ssthresh_) { // slow-start
00241                     // adj cwnd every other rtt
00242                     v_inc_flag_ = !v_inc_flag_;
00243                     if(!v_inc_flag_)
00244                         v_incr_ = 0;
00245                     else {
00246                         if(delta > v_gamma_) {
00247                         // slow-down a bit to ensure
00248                         // the net is not so congested
00249                         ssthresh_ = 2;
00250                         cwnd_-=(cwnd_/8);
00251                         if(cwnd_<2)
00252                             cwnd_ = 2.;
00253                         v_incr_ = 0;
00254                         } else 
00255                         v_incr_ = 1;
00256                     }
00257                 } else { // congestion avoidance
00258                     if(delta>v_beta_) {
00259                         /*
00260                          * slow down a bit, retrack
00261                          * back to prev. rtt's cwnd
00262                          * and dont incr in the nxt rtt
00263                          */
00264                         --cwnd_;
00265                         if(cwnd_<2) cwnd_ = 2;
00266                         v_incr_ = 0;
00267                     } else if(delta<v_alpha_)
00268                         // delta<alpha, faster....
00269                         v_incr_ = 1/cwnd_;
00270                     else // current rate is cool.
00271                         v_incr_ = 0;
00272                 }
00273             } // end of if(rtt > 0)
00274 
00275             // tag the next packet 
00276             v_begseq_ = t_seqno_; 
00277             v_begtime_ = currentTime;
00278         } // end of once per-rtt section
00279 
00280         /* since we set how much to incr only once per rtt,
00281          * need to check if we surpass ssthresh during slow-start
00282          * before the rtt is over.
00283          */     
00284         if(v_incr_ == 1 && cwnd_ >= ssthresh_)
00285             v_incr_ = 0;
00286         
00287         /*
00288          * incr cwnd unless we havent been able to keep up with it
00289          */
00290         if(v_incr_>0 && (cwnd_-(t_seqno_-last_ack_))<=2)
00291             cwnd_ = cwnd_+v_incr_;  
00292 
00293         // Add to make Vegas obey maximum congestion window variable.
00294         if (maxcwnd_ && (int(cwnd_) > maxcwnd_)) {
00295             cwnd_ = maxcwnd_;
00296         }
00297 
00298         /*
00299          * See if we need to update the fine grained timeout value,
00300          * v_timeout_
00301          */
00302 
00303         // reset v_sendtime for acked pkts and incr v_transmits_
00304         double sendTime = v_sendtime_[tcph->seqno()%v_maxwnd_];
00305         int transmits = v_transmits_[tcph->seqno()% v_maxwnd_];
00306         int range = tcph->seqno() - oldack;
00307         for(int k=((oldack+1) %v_maxwnd_); 
00308             k<=(tcph->seqno()%v_maxwnd_) && range >0 ; 
00309             k=((k+1) % v_maxwnd_), range--) {
00310             v_sendtime_[k] = -1.0;
00311             v_transmits_[k] = 0;
00312         }
00313 
00314         if((sendTime !=0.) && (transmits==1)) {
00315              // update fine-grained timeout value, v_timeout_.
00316             double rtt, n;
00317             rtt = currentTime - sendTime;
00318             v_sumRTT_ += rtt;
00319             ++v_cntRTT_;
00320             if(rtt>0) {
00321                 v_rtt_ = rtt;
00322                 if(v_rtt_ < v_baseRTT_)
00323                     v_baseRTT_ = v_rtt_;
00324                 n = v_rtt_ - v_sa_/8;
00325                 v_sa_ += n;
00326                 n = n<0 ? -n : n;
00327                 n -= v_sd_ / 4;
00328                 v_sd_ += n;
00329                 v_timeout_ = ((v_sa_/4)+v_sd_)/2;
00330                 v_timeout_ += (v_timeout_/16);
00331             }
00332         }
00333 
00334         /* 
00335          * check the 1st or 2nd acks after dup ack received 
00336          */
00337         if(v_worried_>0) {
00338             /*
00339              * check if any pkt has been timeout. if so, 
00340              * retx it. no need to change cwnd since we
00341              * already did.
00342              */
00343             --v_worried_;
00344             int expired=vegas_expire(pkt);
00345             if(expired>=0) {
00346                 dupacks_ = numdupacks_;
00347                 output(expired, TCP_REASON_DUPACK);
00348             } else
00349                 v_worried_ = 0;
00350         }
00351     } else if (tcph->seqno() == last_ack_)  {
00352         /* check if a timeout should happen */
00353         ++dupacks_; 
00354         int expired=vegas_expire(pkt);
00355         if (expired>=0 || dupacks_ == numdupacks_) {
00356             double sendTime=v_sendtime_[(last_ack_+1) % v_maxwnd_]; 
00357             int transmits=v_transmits_[(last_ack_+1) % v_maxwnd_];
00358                         /* The line below, for "bug_fix_" true, avoids
00359                         * problems with multiple fast retransmits after
00360             * a retransmit timeout.
00361                         */
00362             if ( !bug_fix_ || (highest_ack_ > recover_) || \
00363                 ( last_cwnd_action_ != CWND_ACTION_TIMEOUT)) {
00364                 int win = window();
00365                 last_cwnd_action_ = CWND_ACTION_DUPACK;
00366                 recover_ = maxseq_;
00367                 /* check for timeout after recv a new ack */
00368                 v_worried_ = MIN(2, t_seqno_ - last_ack_ );
00369         
00370                 /* v_rto expon. backoff */
00371                 if(transmits > 1) 
00372                     v_timeout_ *=2.; 
00373                 else
00374                     v_timeout_ += (v_timeout_/8.);
00375                 /*
00376                  * if cwnd hasnt changed since the pkt was sent
00377                  * we need to decr it.
00378                  */
00379                 if(t_cwnd_changed_ < sendTime ) {
00380                     if(win<=3)
00381                         win=2;
00382                     else if(transmits > 1)
00383                         win >>=1;
00384                     else 
00385                         win -= (win>>2);
00386 
00387                     // record cwnd_
00388                     v_newcwnd_ = double(win);
00389                     // inflate cwnd_
00390                     cwnd_ = v_newcwnd_ + dupacks_;
00391                     t_cwnd_changed_ = currentTime;
00392                 } 
00393 
00394                 // update coarser grained rto
00395                 reset_rtx_timer(1);
00396                 if(expired>=0) 
00397                     output(expired, TCP_REASON_DUPACK);
00398                 else
00399                     output(last_ack_ + 1, TCP_REASON_DUPACK);
00400                      
00401                 if(transmits==1) 
00402                     dupacks_ = numdupacks_;
00403                         }
00404         } else if (dupacks_ > numdupacks_) 
00405             ++cwnd_;
00406     }
00407     Packet::free(pkt);
00408 
00409 #if 0
00410     if (trace_)
00411         plot();
00412 #endif /* 0 */
00413 
00414     /*
00415      * Try to send more data
00416      */
00417     if (dupacks_ == 0 || dupacks_ > numdupacks_ - 1)
00418         send_much(0, 0, maxburst_);
00419 }
00420 
00421 void
00422 VegasTcpAgent::timeout(int tno)
00423 {
00424     if (tno == TCP_TIMER_RTX) {
00425         if (highest_ack_ == maxseq_ && !slow_start_restart_) {
00426             /*
00427              * TCP option:
00428              * If no outstanding data, then don't do anything.
00429              *
00430              * Note:  in the USC implementation,
00431              * slow_start_restart_ == 0.
00432              * I don't know what the U. Arizona implementation
00433              * defaults to.
00434              */
00435             return;
00436         };
00437         dupacks_ = 0;
00438         recover_ = maxseq_;
00439         last_cwnd_action_ = CWND_ACTION_TIMEOUT;
00440         reset_rtx_timer(0);
00441         ++nrexmit_;
00442         slowdown(CLOSE_CWND_RESTART|CLOSE_SSTHRESH_HALF);
00443         cwnd_ = double(v_slowstart_);
00444         v_newcwnd_ = 0;
00445         t_cwnd_changed_ = vegastime();
00446         send_much(0, TCP_REASON_TIMEOUT);
00447     } else {
00448         /* delayed-sent timer, with random overhead to avoid
00449          * phase effect. */
00450         send_much(1, TCP_REASON_TIMEOUT);
00451     };
00452 }
00453 
00454 void
00455 VegasTcpAgent::output(int seqno, int reason)
00456 {
00457     Packet* p = allocpkt();
00458     hdr_tcp *tcph = hdr_tcp::access(p);
00459     double now = Scheduler::instance().clock();
00460     tcph->seqno() = seqno;
00461     tcph->ts() = now;
00462     tcph->reason() = reason;
00463 
00464     /* if this is the 1st pkt, setup senttime[] and transmits[]
00465      * I alloc mem here, instrad of in the constructor, to cover
00466      * cases which windows get set by each different tcp flows */
00467     if (seqno==0) {
00468         v_maxwnd_ = int(wnd_);
00469         if (v_sendtime_)
00470             delete []v_sendtime_;
00471             if (v_transmits_)
00472                     delete []v_transmits_;
00473         v_sendtime_ = new double[v_maxwnd_];
00474         v_transmits_ = new int[v_maxwnd_];
00475         for(int i=0;i<v_maxwnd_;i++) {
00476             v_sendtime_[i] = -1.;
00477             v_transmits_[i] = 0;
00478         }
00479     }
00480 
00481     // record a find grained send time and # of transmits 
00482     int index = seqno % v_maxwnd_;
00483     v_sendtime_[index] = vegastime();  
00484     ++v_transmits_[index];
00485 
00486     /* support ndatabytes_ in output - Lloyd Wood 14 March 2000 */
00487     int bytes = hdr_cmn::access(p)->size(); 
00488     ndatabytes_ += bytes; 
00489     ndatapack_++; // Added this - Debojyoti 12th Oct 2000
00490     send(p, 0);
00491     if (seqno == curseq_ && seqno > maxseq_)
00492         idle();  // Tell application I have sent everything so far
00493 
00494     if (seqno > maxseq_) {
00495         maxseq_ = seqno;
00496         if (!rtt_active_) {
00497             rtt_active_ = 1;
00498             if (seqno > rtt_seq_) {
00499                 rtt_seq_ = seqno;
00500                 rtt_ts_ = now;
00501             }
00502         }
00503     } else {
00504         ++nrexmitpack_;
00505             nrexmitbytes_ += bytes;
00506         }
00507 
00508     if (!(rtx_timer_.status() == TIMER_PENDING))
00509         /* No timer pending.  Schedule one. */
00510         set_rtx_timer();
00511 }
00512 
00513 /*
00514  * return -1 if the oldest sent pkt has not been timeout (based on
00515  * fine grained timer).
00516  */
00517 int
00518 VegasTcpAgent::vegas_expire(Packet* pkt)
00519 {
00520     hdr_tcp *tcph = hdr_tcp::access(pkt);
00521     double elapse = vegastime() - v_sendtime_[(tcph->seqno()+1)%v_maxwnd_];
00522     if (elapse >= v_timeout_) {
00523         return(tcph->seqno()+1);
00524     }
00525     return(-1);
00526 }
00527 

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