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 Mar 6 16:47:52 2007 for ns2 Network Simulator 2.29 by  doxygen 1.4.6