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

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

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