3 * congestion control logic for the swift protocol
5 * Created by Victor Grishchenko on 12/10/09.
6 * Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
13 using namespace swift;
16 tint Channel::MIN_DEV = 50*TINT_MSEC;
17 tint Channel::MAX_SEND_INTERVAL = TINT_SEC*58;
18 tint Channel::LEDBAT_TARGET = TINT_MSEC*25;
19 float Channel::LEDBAT_GAIN = 1.0/LEDBAT_TARGET;
20 tint Channel::LEDBAT_DELAY_BIN = TINT_SEC*30;
21 tint Channel::MAX_POSSIBLE_RTT = TINT_SEC*10;
22 const char* Channel::SEND_CONTROL_MODES[] = {"keepalive", "pingpong",
23 "slowstart", "standard_aimd", "ledbat", "closing"};
26 tint Channel::NextSendTime () {
27 TimeoutDataOut(); // precaution to know free cwnd
28 switch (send_control_) {
29 case KEEP_ALIVE_CONTROL: return KeepAliveNextSendTime();
30 case PING_PONG_CONTROL: return PingPongNextSendTime();
31 case SLOW_START_CONTROL: return SlowStartNextSendTime();
32 case AIMD_CONTROL: return AimdNextSendTime();
33 case LEDBAT_CONTROL: return LedbatNextSendTime();
34 case CLOSE_CONTROL: return TINT_NEVER;
35 default: fprintf(stderr,"send_control.cpp: unknown control %d\n", send_control_); return TINT_NEVER;
39 tint Channel::SwitchSendControl (int control_mode) {
40 dprintf("%s #%u sendctrl switch %s->%s\n",tintstr(),id(),
41 SEND_CONTROL_MODES[send_control_],SEND_CONTROL_MODES[control_mode]);
42 switch (control_mode) {
43 case KEEP_ALIVE_CONTROL:
44 send_interval_ = rtt_avg_; //max(TINT_SEC/10,rtt_avg_);
45 dev_avg_ = max(TINT_SEC,rtt_avg_);
46 data_out_cap_ = bin_t::ALL;
49 case PING_PONG_CONTROL:
50 dev_avg_ = max(TINT_SEC,rtt_avg_);
51 data_out_cap_ = bin_t::ALL;
54 case SLOW_START_CONTROL:
66 send_control_ = control_mode;
67 return NextSendTime();
70 tint Channel::KeepAliveNextSendTime () {
71 if (sent_since_recv_>=3 && last_recv_time_<NOW-3*MAX_SEND_INTERVAL)
72 return SwitchSendControl(CLOSE_CONTROL);
74 return SwitchSendControl(SLOW_START_CONTROL);
75 if (data_in_.time!=TINT_NEVER)
77 /* Gertjan fix 5f51e5451e3785a74c058d9651b2d132c5a94557
78 "Do not increase send interval in keep-alive mode when previous Reschedule
79 was already in the future.
80 The problem this solves is that when we keep on receiving packets in keep-alive
81 mode, the next packet will be pushed further and further into the future, which is
82 not what we want. The scheduled time for the next packet should be unchanged
85 if (!reverse_pex_out_.empty())
86 return reverse_pex_out_.front().time;
87 if (NOW < next_send_time_)
88 return next_send_time_;
90 // Arno: Fix that doesn't do exponential growth always, only after sends
91 // without following recvs
93 // fprintf(stderr,"KeepAliveNextSendTime: gotka %d sentka %d ss %d si %lli\n", lastrecvwaskeepalive_, lastsendwaskeepalive_, sent_since_recv_, send_interval_);
95 if (lastrecvwaskeepalive_ && lastsendwaskeepalive_)
99 else if (lastrecvwaskeepalive_ || lastsendwaskeepalive_)
101 // Arno, 2011-11-29: we like eachother again, start fresh
102 // Arno, 2012-01-25: Unless we're talking to a dead peer.
103 if (sent_since_recv_ < 4) {
104 send_interval_ = rtt_avg_;
106 send_interval_ <<= 1;
108 else if (sent_since_recv_ <= 1)
110 send_interval_ = rtt_avg_;
112 else if (sent_since_recv_ > 1)
114 send_interval_ <<= 1;
116 if (send_interval_>MAX_SEND_INTERVAL)
117 send_interval_ = MAX_SEND_INTERVAL;
118 return last_send_time_ + send_interval_;
121 tint Channel::PingPongNextSendTime () { // FIXME INFINITE LOOP
122 if (dgrams_sent_>=10)
123 return SwitchSendControl(KEEP_ALIVE_CONTROL);
124 if (ack_rcvd_recent_)
125 return SwitchSendControl(SLOW_START_CONTROL);
126 if (data_in_.time!=TINT_NEVER)
128 if (last_recv_time_>last_send_time_)
130 if (!last_send_time_)
132 return last_send_time_ + ack_timeout(); // timeout
135 tint Channel::CwndRateNextSendTime () {
136 if (data_in_.time!=TINT_NEVER)
137 return NOW; // TODO: delayed ACKs
138 //if (last_recv_time_<NOW-rtt_avg_*4)
139 // return SwitchSendControl(KEEP_ALIVE_CONTROL);
140 send_interval_ = rtt_avg_/cwnd_;
141 if (send_interval_>max(rtt_avg_,TINT_SEC)*4)
142 return SwitchSendControl(KEEP_ALIVE_CONTROL);
143 if (data_out_.size()<cwnd_) {
144 dprintf("%s #%u sendctrl next in %llius (cwnd %.2f, data_out %i)\n",
145 tintstr(),id_,send_interval_,cwnd_,(int)data_out_.size());
146 return last_data_out_time_ + send_interval_;
148 assert(data_out_.front().time!=TINT_NEVER);
149 return data_out_.front().time + ack_timeout();
153 void Channel::BackOffOnLosses (float ratio) {
154 ack_rcvd_recent_ = 0;
155 ack_not_rcvd_recent_ = 0;
156 if (last_loss_time_<NOW-rtt_avg_) {
158 last_loss_time_ = NOW;
159 dprintf("%s #%u sendctrl backoff %3.2f\n",tintstr(),id_,cwnd_);
163 tint Channel::SlowStartNextSendTime () {
164 if (ack_not_rcvd_recent_) {
166 return SwitchSendControl(LEDBAT_CONTROL);//AIMD_CONTROL);
168 if (rtt_avg_/cwnd_<TINT_SEC/10)
169 return SwitchSendControl(LEDBAT_CONTROL);//AIMD_CONTROL);
170 cwnd_+=ack_rcvd_recent_;
172 return CwndRateNextSendTime();
175 tint Channel::AimdNextSendTime () {
176 if (ack_not_rcvd_recent_)
178 if (ack_rcvd_recent_) {
180 cwnd_ += ack_rcvd_recent_/cwnd_;
185 return CwndRateNextSendTime();
188 tint Channel::LedbatNextSendTime () {
189 float oldcwnd = cwnd_;
191 tint owd_cur(TINT_NEVER), owd_min(TINT_NEVER);
192 for(int i=0; i<4; i++) {
193 if (owd_min>owd_min_bins_[i])
194 owd_min = owd_min_bins_[i];
195 if (owd_cur>owd_current_[i])
196 owd_cur = owd_current_[i];
198 if (ack_not_rcvd_recent_)
199 BackOffOnLosses(0.8);
200 ack_rcvd_recent_ = 0;
201 tint queueing_delay = owd_cur - owd_min;
202 tint off_target = LEDBAT_TARGET - queueing_delay;
203 cwnd_ += LEDBAT_GAIN * off_target / cwnd_;
206 if (owd_cur==TINT_NEVER || owd_min==TINT_NEVER)
209 //Arno, 2012-02-02: Somehow LEDBAT gets stuck at cwnd_ == 1 sometimes
210 // This hack appears to work to get it back on the right track quickly.
211 if (oldcwnd == 1 && cwnd_ == 1)
215 if (cwnd_count1_ > 10)
217 dprintf("%s #%u sendctrl ledbat stuck, reset\n",tintstr(),id() );
219 for(int i=0; i<4; i++) {
220 owd_min_bins_[i] = TINT_NEVER;
221 owd_current_[i] = TINT_NEVER;
225 dprintf("%s #%u sendctrl ledbat %lli-%lli => %3.2f\n",
226 tintstr(),id_,owd_cur,owd_min,cwnd_);
227 return CwndRateNextSendTime();