tcp-rbp.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-rbp.cc
00005  * Copyright (C) 1997 by the University of Southern California
00006  * $Id: tcp-rbp.cc,v 1.22 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  * Tcp-vegas with Rate-based pacing by John Heidemann <johnh@isi.edu>
00050  * and Vikram Visweswaraiah <visweswa@isi.edu>.
00051  * The original SunOS implementation was by Vikram Visweswaraiah
00052  * and Ashish Savla <asavla@usc.edu>.
00053  *
00054  * Rate-based pacing is an experimental addition to TCP
00055  * to address the slow-start restart problem.
00056  * See <http://www.isi.edu/lsam/publications/rate_based_pacing/index.html>
00057  * for details.
00058  *
00059  * A paper analysing RBP performance is in progress (as of 19-Jun-97).
00060  */
00061 
00062 
00063 #ifndef lint
00064 static const char rcsid[] =
00065 "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/tcp/tcp-rbp.cc,v 1.22 2005/08/25 18:58:12 johnh Exp $ (NCSU/IBM)";
00066 #endif
00067 
00068 #include <stdio.h>
00069 #include <stdlib.h>
00070 #include <sys/types.h>
00071 
00072 #include "ip.h"
00073 #include "tcp.h"
00074 #include "flags.h"
00075 
00076 #ifndef MIN
00077 #define MIN(x, y) ((x)<(y) ? (x) : (y))
00078 #endif /* ! MIN */
00079 
00080 #if 0
00081 #define RBP_DEBUG_PRINTF(x) printf x
00082 #else /* ! 0 */
00083 #define RBP_DEBUG_PRINTF(x)
00084 #endif /* 0 */
00085 
00086 
00087 #define RBP_MIN_SEGMENTS 2
00088 
00089 class RBPVegasTcpAgent;
00090 
00091 class RBPVegasPaceTimer : public TimerHandler {
00092 public:
00093     RBPVegasPaceTimer(RBPVegasTcpAgent *a) : TimerHandler() { a_ = a; }
00094 protected:
00095     virtual void expire(Event *e);
00096     RBPVegasTcpAgent *a_;
00097 };
00098 // Hmmm... ``a is a'' in the construction of the RBPVegasPaceTimer edifice :->
00099 
00100 class RBPVegasTcpAgent : public virtual VegasTcpAgent {
00101     friend class RBPVegasPaceTimer;
00102  public:
00103     RBPVegasTcpAgent();
00104     virtual void recv(Packet *pkt, Handler *);
00105     virtual void timeout(int tno);
00106     virtual void send_much(int force, int reason, int maxburst);
00107 
00108     double rbp_scale_;   // conversion from actual -> rbp send rates
00109     enum rbp_rate_algorithms { RBP_NO_ALGORITHM, RBP_VEGAS_RATE_ALGORITHM, RBP_CWND_ALGORITHM };
00110     int rbp_rate_algorithm_;
00111 protected:
00112     void paced_send_one();
00113     int able_to_rbp_send_one();
00114 
00115     // stats on what we did
00116     int rbp_segs_actually_paced_;
00117 
00118     enum rbp_modes { RBP_GOING, RBP_POSSIBLE, RBP_OFF };
00119     enum rbp_modes rbp_mode_;
00120     double rbp_inter_pace_delay_;
00121     RBPVegasPaceTimer pace_timer_;
00122 };
00123 
00124 static class RBPVegasTcpClass : public TclClass {
00125 public:
00126     RBPVegasTcpClass() : TclClass("Agent/TCP/Vegas/RBP") {}
00127     TclObject* create(int, const char*const*) {
00128         return (new RBPVegasTcpAgent());
00129     }
00130 } class_vegas_rbp;
00131 
00132 
00133 void RBPVegasPaceTimer::expire(Event *) { a_->paced_send_one(); }
00134 
00135 
00136 RBPVegasTcpAgent::RBPVegasTcpAgent() : VegasTcpAgent(),
00137     rbp_mode_(RBP_OFF),
00138     pace_timer_(this)
00139 {
00140     bind("rbp_scale_", &rbp_scale_);
00141     bind("rbp_rate_algorithm_", &rbp_rate_algorithm_);
00142     bind("rbp_segs_actually_paced_", &rbp_segs_actually_paced_);
00143     bind("rbp_inter_pace_delay_", &rbp_inter_pace_delay_);
00144 }
00145 
00146 void
00147 RBPVegasTcpAgent::recv(Packet *pkt, Handler *hand)
00148 {
00149     if (rbp_mode_ != RBP_OFF) {
00150         // reciept of anything disables rbp
00151         rbp_mode_ = RBP_OFF;
00152         // Vegas takes care of cwnd.
00153     };
00154     VegasTcpAgent::recv(pkt, hand);
00155 }
00156 
00157 void
00158 RBPVegasTcpAgent::timeout(int tno)
00159 {
00160     if (tno == TCP_TIMER_RTX) {
00161         if (highest_ack_ == maxseq_) {
00162             // Idle for a while => RBP next time.
00163             rbp_mode_ = RBP_POSSIBLE;
00164             return;
00165         };
00166     };
00167     VegasTcpAgent::timeout(tno);
00168 }
00169 
00170 void
00171 RBPVegasTcpAgent::send_much(int force, int reason, int maxburst)
00172 {
00173     if (rbp_mode_ == RBP_POSSIBLE && able_to_rbp_send_one()) {
00174         // start paced mode
00175         rbp_mode_ = RBP_GOING; 
00176         rbp_segs_actually_paced_ = 0;
00177         double rbwin_vegas;
00178         switch (rbp_rate_algorithm_) {
00179         case RBP_VEGAS_RATE_ALGORITHM:
00180             // Try to follow tcp_output.c here
00181             // Calculate the vegas window as its reported rate
00182             // times the rtt.
00183             rbwin_vegas = v_actual_ * v_rtt_;
00184             RBP_DEBUG_PRINTF(("-----------------\n"));
00185             RBP_DEBUG_PRINTF(("rbwin_vegas = %g\nv_actual = %g\nv_rtt =%g\nbase_rtt=%g\n",
00186                       rbwin_vegas, v_actual_, v_rtt_, v_baseRTT_));
00187             // Smooth the vegas window
00188             rbwin_vegas *= rbp_scale_;
00189             break;
00190         case RBP_CWND_ALGORITHM:
00191             // Pace out scaled cwnd.
00192             rbwin_vegas = cwnd_ * rbp_scale_;
00193             break;
00194         default:
00195             // quiet the compiler.
00196             rbwin_vegas = 0.0;
00197             abort();
00198         };
00199         rbwin_vegas = int(rbwin_vegas + 0.5);   // round
00200         // Always pace at least RBP_MIN_SEGMENTS
00201         if (rbwin_vegas <= RBP_MIN_SEGMENTS) {
00202             rbwin_vegas = RBP_MIN_SEGMENTS;
00203         };
00204 
00205         // Conservatively set the congestion window to min of
00206         // congestion window and the smoothed rbwin_vegas
00207         RBP_DEBUG_PRINTF(("cwnd before check = %g\n", double(cwnd_)));
00208         cwnd_ = MIN(cwnd_,(TracedDouble) rbwin_vegas);
00209         RBP_DEBUG_PRINTF(("cwnd after check = %g\n", double(cwnd_)));
00210         RBP_DEBUG_PRINTF(("recv win = %g\n", wnd_));
00211         // RBP timer calculations must be based on the actual
00212         // window which is the min of the receiver's
00213         // advertised window and the congestion window.
00214         // TcpAgent::window() does this job.
00215         // What this means is we expect to send window() pkts
00216         // in v_rtt_ time.
00217         rbp_inter_pace_delay_ = (v_rtt_)/(window() * 1.0);
00218         RBP_DEBUG_PRINTF(("window is %d\n", window()));
00219         RBP_DEBUG_PRINTF(("ipt = %g\n", rbp_inter_pace_delay_));
00220         paced_send_one();
00221     } else {
00222         VegasTcpAgent::send_much(force,reason, maxburst);
00223     }
00224 }
00225 
00226 void
00227 RBPVegasTcpAgent::paced_send_one()
00228 {
00229     if (rbp_mode_ == RBP_GOING && able_to_rbp_send_one()) {
00230         RBP_DEBUG_PRINTF(("Sending one rbp packet\n"));
00231         // send one packet
00232         output(t_seqno_++, TCP_REASON_RBP);
00233         rbp_segs_actually_paced_++;
00234         // schedule next pkt
00235         pace_timer_.resched(rbp_inter_pace_delay_);
00236     };
00237 }
00238 
00239 int
00240 RBPVegasTcpAgent::able_to_rbp_send_one()
00241 {
00242     return t_seqno_ < curseq_ && t_seqno_ <= highest_ack_ + window();
00243 }
00244 
00245 
00246 /***********************************************************************
00247  *
00248  * The reno-based version
00249  *
00250  */
00251 
00252 
00253 class RBPRenoTcpAgent;
00254 
00255 class RBPRenoPaceTimer : public TimerHandler {
00256 public:
00257     RBPRenoPaceTimer(RBPRenoTcpAgent *a) : TimerHandler() { a_ = a; }
00258 protected:
00259     virtual void expire(Event *e);
00260     RBPRenoTcpAgent *a_;
00261 };
00262 // Hmmm... ``a is a'' in the construction of the RBPRenoPaceTimer edifice :->
00263 
00264 class RBPRenoTcpAgent : public virtual RenoTcpAgent {
00265     friend class RBPRenoPaceTimer;
00266  public:
00267     RBPRenoTcpAgent();
00268     virtual void recv(Packet *pkt, Handler *);
00269     virtual void timeout(int tno);
00270     virtual void send_much(int force, int reason, int maxburst);
00271 
00272     double rbp_scale_;   // conversion from actual -> rbp send rates
00273     // enum rbp_rate_algorithms { RBP_NO_ALGORITHM, RBP_VEGAS_RATE_ALGORITHM, RBP_CWND_ALGORITHM };
00274     // int rbp_rate_algorithm_;
00275 protected:
00276     void paced_send_one();
00277     int able_to_rbp_send_one();
00278 
00279     // stats on what we did
00280     int rbp_segs_actually_paced_;
00281 
00282     enum rbp_modes { RBP_GOING, RBP_POSSIBLE, RBP_OFF };
00283     enum rbp_modes rbp_mode_;
00284     double rbp_inter_pace_delay_;
00285     RBPRenoPaceTimer pace_timer_;
00286 };
00287 
00288 static class RBPRenoTcpClass : public TclClass {
00289 public:
00290     RBPRenoTcpClass() : TclClass("Agent/TCP/Reno/RBP") {}
00291     TclObject* create(int, const char*const*) {
00292         return (new RBPRenoTcpAgent());
00293     }
00294 } class_reno_rbp;
00295 
00296 
00297 void RBPRenoPaceTimer::expire(Event *) { a_->paced_send_one(); }
00298 
00299 RBPRenoTcpAgent::RBPRenoTcpAgent() : TcpAgent(),
00300     rbp_mode_(RBP_OFF),
00301     pace_timer_(this)
00302 {
00303     bind("rbp_scale_", &rbp_scale_);
00304     // algorithm is not used in Reno
00305     // bind("rbp_rate_algorithm_", &rbp_rate_algorithm_);
00306     bind("rbp_segs_actually_paced_", &rbp_segs_actually_paced_);
00307     bind("rbp_inter_pace_delay_", &rbp_inter_pace_delay_);
00308 }
00309 
00310 void
00311 RBPRenoTcpAgent::recv(Packet *pkt, Handler *hand)
00312 {
00313     if (rbp_mode_ != RBP_OFF) {
00314         // reciept of anything disables rbp
00315         rbp_mode_ = RBP_OFF;
00316 
00317         // reset cwnd such that we're now ack clocked.
00318         hdr_tcp *tcph = hdr_tcp::access(pkt);
00319         if (tcph->seqno() > last_ack_) {
00320             /* reno does not do rate adjustments as Vegas;
00321              * normally, one wouldn't do any adjustments to
00322              * cwnd and allow the sliding window to do its job
00323              * But, if cwnd >> amt_paced, then there's a
00324              * bunch of data that can be sent asap, plus the
00325              * two (typically, due to delacks) that get opened
00326              * up due to the first ack. This would lead to
00327              * a burst, defeating the purpose of pacing.
00328              * Ideally, one would want cwnd = amt_paced
00329              * ALWAYS. Since this doesn't necessarily happen,
00330              * `cap' cwnd to the amt paced and THEN let
00331              * sliding windows take over. Note that this
00332              * mechanism will typically result in 3 segs
00333              * being sent out when the first ack is received.
00334              */
00335             cwnd_ = maxseq_ - last_ack_;
00336             RBP_DEBUG_PRINTF(("\ncwnd-after-first-ack=%g\n", (double)cwnd_));
00337         };
00338 
00339     };
00340     RenoTcpAgent::recv(pkt, hand);
00341 }
00342 
00343 void
00344 RBPRenoTcpAgent::timeout(int tno)
00345 {
00346     if (tno == TCP_TIMER_RTX) {
00347         if (highest_ack_ == maxseq_) {
00348             // Idle for a while => RBP next time.
00349             rbp_mode_ = RBP_POSSIBLE;
00350             return;
00351         };
00352     };
00353     RenoTcpAgent::timeout(tno);
00354 }
00355 
00356 void
00357 RBPRenoTcpAgent::send_much(int force, int reason, int maxburst)
00358 {
00359     if (rbp_mode_ == RBP_POSSIBLE && able_to_rbp_send_one()) {
00360         // start paced mode
00361         rbp_mode_ = RBP_GOING; 
00362         rbp_segs_actually_paced_ = 0;
00363 
00364         // Pace out scaled cwnd.
00365         double rbwin_reno;
00366         rbwin_reno = cwnd_ * rbp_scale_;
00367 
00368         rbwin_reno = int(rbwin_reno + 0.5);   // round
00369         // Always pace at least RBP_MIN_SEGMENTS
00370         if (rbwin_reno <= RBP_MIN_SEGMENTS) {
00371             rbwin_reno = RBP_MIN_SEGMENTS;
00372         };
00373 
00374         // Conservatively set the congestion window to min of
00375         // congestion window and the smoothed rbwin_reno
00376         RBP_DEBUG_PRINTF(("cwnd before check = %g\n", double(cwnd_)));
00377         cwnd_ = MIN(cwnd_,(TracedDouble) rbwin_reno);
00378         RBP_DEBUG_PRINTF(("cwnd after check = %g\n", double(cwnd_)));
00379         RBP_DEBUG_PRINTF(("recv win = %g\n", wnd_));
00380         // RBP timer calculations must be based on the actual
00381         // window which is the min of the receiver's
00382         // advertised window and the congestion window.
00383         // TcpAgent::window() does this job.
00384         // What this means is we expect to send window() pkts
00385         // in v_srtt_ time.
00386         static double srtt_scale = 0.0;
00387         if (srtt_scale == 0.0) {  // yuck yuck yuck!
00388             srtt_scale = 1.0; // why are we doing fixed point?
00389             int i;
00390             for (i = T_SRTT_BITS; i > 0; i--) {
00391                 srtt_scale /= 2.0;
00392             };
00393         }
00394         rbp_inter_pace_delay_ = (t_srtt_ * srtt_scale * tcp_tick_) / (window() * 1.0);
00395         RBP_DEBUG_PRINTF(("window is %d\n", window()));
00396         RBP_DEBUG_PRINTF(("ipt = %g\n", rbp_inter_pace_delay_));
00397         paced_send_one();
00398     } else {
00399         RenoTcpAgent::send_much(force,reason, maxburst);
00400     };
00401 }
00402 
00403 void
00404 RBPRenoTcpAgent::paced_send_one()
00405 {
00406     if (rbp_mode_ == RBP_GOING && able_to_rbp_send_one()) {
00407         RBP_DEBUG_PRINTF(("Sending one rbp packet\n"));
00408         // send one packet
00409         output(t_seqno_++, TCP_REASON_RBP);
00410         rbp_segs_actually_paced_++;
00411         // schedule next pkt
00412         pace_timer_.resched(rbp_inter_pace_delay_);
00413     };
00414 }
00415 
00416 int
00417 RBPRenoTcpAgent::able_to_rbp_send_one()
00418 {
00419     return t_seqno_ < curseq_ && t_seqno_ <= highest_ack_ + window();
00420 }

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