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

tcp-sack-rh.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 are permitted
00007  * provided that the above copyright notice and this paragraph are
00008  * duplicated in all such forms and that any documentation,
00009  * advertising materials, and other materials related to such
00010  * distribution and use acknowledge that the software was developed
00011  * by the University of California, Lawrence Berkeley Laboratory,
00012  * Berkeley, CA.  The name of the University may not be used to
00013  * endorse or promote products derived from this software without
00014  * specific prior written permission.
00015  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
00016  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
00017  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00018  */
00019 
00020 /*
00021  * This is TCP SACK with Rate-Halving, contributed code from Matt
00022  * Mathis, Jeff Semke, Jamshid Mahdavi, and Kevin Lahey, and
00023  * described at "http://www.psc.edu/networking/rate-halving/". 
00024  */
00025 
00026 #ifndef lint
00027 static const char rcsid[] =
00028     "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/tcp/tcp-sack-rh.cc,v 1.5 2002/10/09 03:47:02 difa Exp $ (PSC-SACKRH)";
00029 #endif
00030 
00031 #include <stdio.h>
00032 #include <stdlib.h>
00033 #include <sys/types.h>
00034 
00035 #include "ip.h"
00036 #include "tcp.h"
00037 #include "flags.h"
00038 #include "scoreboard-rh.h"
00039 #include "random.h"
00040 
00041 #define TRUE    1
00042 #define FALSE   0
00043 
00044 #define RH_INCR 0
00045 #define RH_EXACT 11
00046 #define RH_EST 12
00047 #define RH_EST_REPAIR 13
00048 
00049 #define RHEH_NONE 0
00050 #define RHEH_COUNTING 1
00051 #define RHEH_NEW_HOLE 2
00052 #define RHEH_RETRANS_OCCURRED 3
00053 
00054 //#define DEBUGRH 
00055 
00056 class SackRHTcpAgent : public TcpAgent {
00057  public:
00058         SackRHTcpAgent();
00059         virtual int window();
00060         virtual void recv(Packet *pkt, Handler*);
00061         virtual void timeout(int tno);
00062         void plot();
00063         virtual void send_much(int force, int reason, int maxburst);
00064         virtual void boundparms();
00065         virtual void estadjust();
00066         virtual void rhclear();
00067         virtual void computefack();
00068         virtual void SackRHTcpAgent::newack(Packet* pkt);
00069  protected:
00070         int fack_;                /* the FACK state variable  */
00071         int retran_data_;         /* the number of retransmitted packets in the pipe  */
00072         int num_dupacks_;         /* sigh, I think we still want this for the NewReno case */
00073         int rh_state_;            /* The rate halving state we are in  */
00074         double prior_cwnd_;       /* The value of cwnd at the start of RH  */
00075         int prior_max_seq_;       /* The old value of max_seq  */
00076         int rh_id_;               /* A unique ID for the current adjustment interval  */
00077         int rh_max_;              /* The max ID which has been used so far  */
00078         int rh_est_hole_state_;   /* Status of NewReno style holes during estimation */
00079         int num_retrans_;         /* Number of retransmissions during the estimation state */
00080         int rh_retran_flag_;      /* Flag to indicate a retransmission has occured */
00081         int rh_ecn_flag_;         /* Flag to indicate an ECN was seen during this recovery */
00082         ScoreBoardRH scb_;
00083 };
00084 
00085 static class SackRHTcpClass : public TclClass {
00086 public:
00087         SackRHTcpClass() : TclClass("Agent/TCP/SackRH") {}
00088         TclObject* create(int, const char*const*) {
00089                 return (new SackRHTcpAgent());
00090         }
00091 } class_sack_rh;
00092 
00093 int SackRHTcpAgent::window()
00094 {
00095         return(cwnd_ < wnd_ ? (int) cwnd_ : (int) wnd_);
00096 }
00097 
00098 SackRHTcpAgent::SackRHTcpAgent() : fack_(-1), 
00099         retran_data_(0), num_dupacks_(0), rh_state_(RH_INCR), rh_id_(0), rh_max_(0),
00100         rh_est_hole_state_(0), rh_retran_flag_(0), rh_ecn_flag_(0), scb_(&numdupacks_)
00101 {
00102 }
00103 
00104 void SackRHTcpAgent::recv(Packet *pkt, Handler*)
00105 {
00106         int old_fack, old_retran_data, old_ack, old_numdupacks;
00107         hdr_tcp *tcph = hdr_tcp::access(pkt);
00108 
00109         int ecnecho = (hdr_flags::access(pkt)->ecnecho() && last_ack_ != -1);
00110         int sackpresent = (tcph->sa_length() > 0);
00111 
00112         ++nackpack_;
00113 
00114 #ifdef DEBUGRH
00115         printf ("ENTER: RH=%02d snd.nxt=%3d fack=%3d ack=%3d retran_data=%03d \n",
00116                 rh_state_, (int)t_seqno_, fack_, last_ack_, retran_data_);
00117         printf ("       prior_max_seq=%3d prior_cwnd=%f rheh=%2d num_dupacks=%2d\n",
00118                 prior_max_seq_, prior_cwnd_, rh_est_hole_state_, num_dupacks_);
00119         printf ("       cwnd:%f=%d curseq=%d\n",
00120                 (float)cwnd_, int(cwnd_), (int)curseq_);
00121 #endif
00122         old_ack = last_ack_;
00123         newack(pkt);
00124 
00125         if (ecnecho) rh_ecn_flag_=1;   /*  Remember that we saw an ECN  */
00126 
00127         /*  Process any SACK blocks:  */
00128         old_fack = fack_;
00129         old_retran_data = retran_data_;
00130         retran_data_ -= scb_.UpdateScoreBoard (last_ack_, tcph, rh_id_);
00131 
00132         old_numdupacks = num_dupacks_;
00133         if (!sackpresent && (old_ack == last_ack_)) {
00134                 num_dupacks_++;
00135                 if ((rh_est_hole_state_ == RHEH_COUNTING) && 
00136                     (num_dupacks_ == numdupacks_)) {
00137                         rh_est_hole_state_ = RHEH_NEW_HOLE;
00138                 }
00139         }
00140 
00141         computefack();
00142 
00143 
00144         /*  7.8  Reordering Exit transitions */
00145         if (!rh_retran_flag_ && !rh_ecn_flag_) {
00146                 if (((rh_state_ == RH_EXACT) && !sackpresent && !ecnecho) ||
00147                     ((rh_state_ == RH_EST) && (last_ack_ > old_ack))) {
00148                         rh_state_ = RH_INCR;
00149                         cwnd_ = prior_cwnd_;
00150                         rhclear();
00151                         computefack();  /* In case we were estimating it before */
00152                 }
00153         }
00154 
00155         /*  7.10 Completion of RH_EXACT  */
00156         if (rh_state_ == RH_EXACT) {
00157                 if ((fack_ >= prior_max_seq_) || scb_.RetranSacked(rh_id_)) {
00158                         boundparms();
00159                         rh_state_ = RH_INCR;
00160                         rhclear();
00161                 }
00162         }
00163 
00164         /*  7.11 Completion of RH_EST*  */
00165         if ((rh_state_ == RH_EST) || (rh_state_ == RH_EST_REPAIR)) {
00166                 if (last_ack_ >= prior_max_seq_) {
00167                         retran_data_ = 0;     /*  Just like a partial ACK, this
00168                                                   means a retransmission has left
00169                                                   the network.  */
00170                         estadjust();
00171                         boundparms();
00172                         rh_state_ = RH_INCR;
00173                         rhclear();
00174                         computefack();   /*  This is needed to restore 
00175                                              fack now that we are done estimating */
00176                 }
00177         }
00178 
00179         /*  7.12 NewReno case  */
00180         if ((rh_state_ == RH_EST) && (cwnd_ <= prior_cwnd_/2)) {
00181                 rh_state_ = RH_EST_REPAIR;
00182         }
00183 
00184         /*  7.9  Processing partial Acks in RH_EST or RH_EST_REPAIR */
00185         if (((rh_state_ == RH_EST) || (rh_state_ == RH_EST_REPAIR))
00186             && (last_ack_ > old_ack)) {
00187                 retran_data_ = 0;
00188                 num_dupacks_ -= (last_ack_ - old_ack - 1);
00189                 if (num_dupacks_ >= numdupacks_) {
00190                         rh_est_hole_state_ = RHEH_NEW_HOLE;
00191                 }
00192                 else {
00193                         /*  We don't have 3 dupacks on this hole, so 
00194                             go back to counting...  */
00195                         rh_est_hole_state_ = RHEH_COUNTING;
00196                 }
00197                 computefack();  /* Recompute the estimate */
00198         }
00199                 
00200         /*  Now check the entry conditions:  */
00201 
00202         /*  7.4  Entering RH_EXACT  */
00203         if ((rh_state_ == RH_INCR) && (ecnecho || scb_.GetNewHoles() != 0)) {
00204                 rh_state_ = RH_EXACT;
00205                 prior_cwnd_ = cwnd_;
00206                 prior_max_seq_ =  t_seqno_;
00207                 cong_action_ = TRUE;
00208                 rh_id_ = ++rh_max_;
00209         }
00210 
00211         /*  7.5  Entering RH_EST  */
00212         if ((rh_state_ == RH_INCR)  && (num_dupacks_ > 0)) {
00213                 rh_state_ = RH_EST;
00214                 rh_est_hole_state_ = RHEH_COUNTING;
00215                 prior_cwnd_ = cwnd_;
00216                 prior_max_seq_ =  t_seqno_;
00217                 cong_action_ = TRUE;
00218                 rh_id_ = ++rh_max_;
00219         }
00220                 
00221         if ((rh_state_ == RH_EXACT)  && (num_dupacks_ > 0)) {
00222                 rh_state_ = RH_EST;
00223                 rh_est_hole_state_ = RHEH_COUNTING;
00224                 /*  cong_action_ = TRUE;  */   /*  Don't need it, 
00225                                                    since we already
00226                                                    sent it  */
00227                 rh_id_ = ++rh_max_;
00228         }
00229 
00230         if ((rh_state_ == RH_EST_REPAIR)  && (ecnecho)) {
00231                 rh_state_ = RH_EST;
00232                 prior_cwnd_ = cwnd_;
00233                 prior_max_seq_ =  t_seqno_;
00234                 cong_action_ = TRUE;
00235                 rh_id_ = ++rh_max_;
00236         }
00237 
00238         /*  Updating the window:  */
00239         switch (rh_state_) {
00240         case RH_INCR:
00241                 /*  Case 7.2:  */
00242                 if ((t_seqno_ - old_fack + old_retran_data + 1) >= (int)cwnd_) {
00243                         opencwnd();
00244                 }
00245                 break;
00246         case RH_EXACT:
00247                 /*  Case 7.6  */
00248                 cwnd_ -= ((float)((fack_ - old_fack) + (scb_.GetNewHoles()))) / 2.0;
00249                 break;
00250         case RH_EST:
00251                 /*  Case 7.7  */
00252                 cwnd_ -= 0.5;
00253                 break;
00254         case RH_EST_REPAIR:
00255                 /*  Do not modify cwnd in this state  */
00256                 break;
00257         }
00258 
00259 #ifdef DEBUGRH
00260         printf ("EXIT:  RH=%02d snd.nxt=%3d fack=%3d ack=%3d retran_data=%03d \n       cwnd:%f=%d curseq=%d\n",
00261                 rh_state_, (int)t_seqno_, fack_, last_ack_, retran_data_, (float)cwnd_, int(cwnd_),
00262                 (int)curseq_);
00263 #endif
00264         send_much(FALSE, 0, maxburst_);
00265 
00266         Packet::free(pkt);
00267 #ifdef notyet
00268         if (trace_)
00269                 plot();
00270 #endif
00271 }
00272 
00273 void SackRHTcpAgent::rhclear()
00274 {
00275         rh_id_ = prior_max_seq_ = num_dupacks_ = rh_est_hole_state_ = 
00276                 rh_ecn_flag_ = num_retrans_ = rh_retran_flag_ = 0;
00277         prior_cwnd_ = 0;
00278 }
00279 
00280 /*  Compute FACK:  */
00281 void SackRHTcpAgent::computefack()
00282 {
00283         if ((rh_state_ == RH_INCR) || (rh_state_ == RH_EXACT)) {
00284                 fack_ = scb_.GetFack (last_ack_);
00285         }
00286         else {
00287                 fack_ = last_ack_ + num_dupacks_ + 1;
00288         }
00289 }
00290 
00291 /*  7.14: Bounding Parameters  */
00292 void SackRHTcpAgent::boundparms()
00293 {
00294         if (cwnd_ > prior_cwnd_/2.0) 
00295                 cwnd_ = prior_cwnd_/2.0;
00296         ssthresh_ = cwnd_;
00297         if (ssthresh_ < prior_cwnd_/4.0)
00298                 ssthresh_ = (int) (prior_cwnd_/4.0);
00299 }
00300 
00301 /*  7.13: Corrections to Estimation  */
00302 void SackRHTcpAgent::estadjust()
00303 {
00304         cwnd_ = (prior_cwnd_ - num_retrans_)/2.0;
00305 }
00306 
00307 /*  7.15: Retransmission Timeout  */
00308 void SackRHTcpAgent::timeout(int tno)
00309 {
00310         /* retransmit timer */
00311         if (tno == TCP_TIMER_RTX) {
00312                 if (highest_ack_ == maxseq_ && !slow_start_restart_)
00313                         /*
00314                          * TCP option:
00315                          * If no outstanding data, then don't do anything.
00316                          */
00317                         return;
00318                 if (highest_ack_ == -1 && wnd_init_option_ == 2)
00319                         /* 
00320                          * First packet dropped, so don't use larger
00321                          * initial windows. 
00322                          */
00323                         wnd_init_option_ = 1;
00324 
00325 #ifdef DEBUGRH
00326                 printf ("timeout. last_ack: %d seqno: %d\n", 
00327                         (int)last_ack_, (int)t_seqno_);
00328 #endif
00329 
00330                 if (rh_state_ != RH_INCR) {
00331                         /*  4.6  We took a timeout in the middle of
00332                             rate-halving.  Pretend like RH
00333                             never started:  */
00334                         cwnd_ = prior_cwnd_;
00335                         rhclear();
00336                         rh_state_ = RH_INCR;
00337                         
00338                 }
00339                 ++nrexmit_;
00340                 slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_RESTART);
00341 
00342                 retran_data_ = 0;
00343                 scb_.TimeoutScoreBoard(t_seqno_);
00344                 computefack();
00345 
00346                 /*  Pull back the sequence number -- pretend you never
00347                     sent packets beyond FACK  */
00348                 if (t_seqno_ > fack_+1) {
00349                         t_seqno_ = fack_+1;
00350                 }
00351                 reset_rtx_timer(1,1);
00352 
00353                 last_cwnd_action_ = CWND_ACTION_TIMEOUT;
00354                 send_much(0, TCP_REASON_TIMEOUT, maxburst_);
00355         } 
00356         else {
00357                 timeout_nonrtx(tno);
00358         }
00359 }
00360 
00361 
00362 void SackRHTcpAgent::send_much(int force, int reason, int maxburst)
00363 {
00364         register int found, npacket = 0;
00365         int xmit_seqno;
00366 
00367         found = 1;
00368         if (!force && delsnd_timer_.status() == TIMER_PENDING)
00369                 return;
00370         /*
00371          * as long as the pipe is open and there is app data to send...
00372          */
00373 
00374         /*  7.3: data transmission  */
00375         while (int(cwnd_) >= (t_seqno_ - fack_ + retran_data_)
00376                 && found) {
00377                 if (overhead_ == 0 || force) {
00378                         found = 0;
00379                         xmit_seqno = scb_.GetNextRetran ();
00380 
00381                         if (xmit_seqno == -1) { 
00382                                 /*  Check to see if we have identified a new hole... */
00383                                 if (((rh_state_ == RH_EST) || (rh_state_ == RH_EST_REPAIR)) &&
00384                                     (rh_est_hole_state_ == RHEH_NEW_HOLE)) {
00385                                         xmit_seqno = last_ack_ + 1;
00386                                         rh_est_hole_state_ = RHEH_RETRANS_OCCURRED;
00387                                         num_retrans_++;
00388                                         retran_data_++;
00389                                         rh_retran_flag_ = 1;
00390                                         found = 1;
00391                                 }
00392                                 else if ((t_seqno_ < curseq_) && 
00393                                          (t_seqno_<=last_ack_+int(wnd_))) {
00394                                         found = 1;
00395                                         xmit_seqno = t_seqno_++;
00396                                 }
00397                         } else if (xmit_seqno<=last_ack_+int(wnd_)) {
00398                                 found = 1;
00399                                 scb_.MarkRetran (xmit_seqno, t_seqno_, rh_id_);
00400                                 retran_data_++;
00401                                 rh_retran_flag_ = 1;
00402                         }
00403                         if (found) {
00404 #ifdef DEBUGRH
00405                                 printf ("      Sending: %3d\n", xmit_seqno);
00406 #endif                          
00407                                 output(xmit_seqno, reason);
00408                                 if (t_seqno_ <= xmit_seqno)
00409                                         t_seqno_ = xmit_seqno + 1;
00410                                 npacket++;
00411                         }
00412                 } else if (!(delsnd_timer_.status() == TIMER_PENDING)) {
00413                         /*
00414                          * Set a delayed send timeout.
00415                          * This is only for the simulator,to add some
00416                          *   randomization if speficied.
00417                          */
00418                         delsnd_timer_.resched(Random::uniform(overhead_));
00419                         return;
00420                 }
00421                 if (maxburst && npacket == maxburst)
00422                         break;
00423         } /* while */
00424 }
00425 
00426 void SackRHTcpAgent::plot()
00427 {
00428 #ifdef notyet
00429         double t = Scheduler::instance().clock();
00430         sprintf(trace_->buffer(), "t %g %d rtt %g\n", 
00431                 t, class_, t_rtt_ * tcp_tick_);
00432         trace_->dump();
00433         sprintf(trace_->buffer(), "t %g %d dev %g\n", 
00434                 t, class_, t_rttvar_ * tcp_tick_);
00435         trace_->dump();
00436         sprintf(trace_->buffer(), "t %g %d win %f\n", t, class_, cwnd_);
00437         trace_->dump();
00438         sprintf(trace_->buffer(), "t %g %d bck %d\n", t, class_, t_backoff_);
00439         trace_->dump();
00440 #endif
00441 }
00442 
00443 /*
00444  * Process a packet that acks previously unacknowleged data.
00445  *
00446  * The only thing we change here is the policy for clearing
00447  * RTO backoff...
00448  *
00449  */
00450 
00451 void SackRHTcpAgent::newack(Packet* pkt)
00452 {
00453         double now = Scheduler::instance().clock();
00454         hdr_tcp *tcph = hdr_tcp::access(pkt);
00455         /* 
00456          * Wouldn't it be better to set the timer *after*
00457          * updating the RTT, instead of *before*? 
00458          */
00459         newtimer(pkt);
00460         last_ack_ = tcph->seqno();
00461         highest_ack_ = last_ack_;
00462 
00463         if (t_seqno_ < last_ack_ + 1)
00464                 t_seqno_ = last_ack_ + 1;
00465         /* 
00466          * Update RTT only if it's OK to do so from info in the flags header.
00467          * This is needed for protocols in which intermediate agents
00468          * in the network intersperse acks (e.g., ack-reconstructors) for
00469          * various reasons (without violating e2e semantics).
00470          */     
00471         hdr_flags *fh = hdr_flags::access(pkt);
00472         if (!fh->no_ts_) {
00473                 if (ts_option_)
00474                         rtt_update(now - tcph->ts_echo());
00475 
00476                 if (rtt_active_ && tcph->seqno() >= rtt_seq_) {
00477                         if (!hdr_flags::access(pkt)->ecnecho()) {
00478                                 /* 
00479                                  * Don't end backoff if there is still an ECN-Echo
00480                                  * on the packet.
00481                                  */
00482                                 t_backoff_ = 1;
00483                         }
00484                         rtt_active_ = 0;
00485                         if (!ts_option_)
00486                                 rtt_update(now - rtt_ts_);
00487                 }
00488         }
00489         /* update average window */
00490         awnd_ *= 1.0 - wnd_th_;
00491         awnd_ += wnd_th_ * cwnd_;
00492 }

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