20#ifdef _TEST_CONGESTION_
24#define clamp(val, lo, hi) min(max(val, lo), hi)
26#define tcp_time_stamp ((UINT32)(pstruEventDetails->dEventTime/1000))
29#define BICTCP_BETA_SCALE 1024
35#define HYSTART_ACK_TRAIN 0x1
36#define HYSTART_DELAY 0x2
39#define HYSTART_MIN_SAMPLES 8
40#define HYSTART_DELAY_MIN (4U<<3)
41#define HYSTART_DELAY_MAX (16U<<3)
42#define HYSTART_DELAY_THRESH(x) clamp(x, HYSTART_DELAY_MIN, HYSTART_DELAY_MAX)
44static int fast_convergence = 1;
45static int tcp_friendliness = 1;
47static int hystart = 1;
48static int hystart_detect = HYSTART_ACK_TRAIN | HYSTART_DELAY;
51static UINT32 cube_rtt_scale;
52static UINT32 beta_scale;
53static UINT32 cube_factor;
57 UINT32 hystart_low_window;
58 int hystart_ack_delta;
61 UINT32 initial_ssthresh;
68 UINT32 bic_origin_point;
94 double lastWinUpdateTime;
97 bool isFastRetransmit;
104 bool isLossRecoveryPhase;
108}CONGESTIONVAR, *PCONGESTIONVAR;
110static inline PCONGESTIONVAR get_congestionvar(PNETSIM_SOCKET s)
112 return (PCONGESTIONVAR)s->tcb->congestionData;
115static inline void set_congestionvar(PNETSIM_SOCKET s, PCONGESTIONVAR data)
117 s->tcb->congestionData = data;
120static void set_cwnd(PNETSIM_SOCKET s, UINT32 newcwnd)
122 PCONGESTIONVAR var = get_congestionvar(s);
123 if (s->tcb->isWindowScaling)
125 UINT32 c = (UINT32)newcwnd;
126 var->
cwnd = (UINT16)(c >> s->tcb->Snd.Wind_Shift);
127 assert(var->
cwnd < 65535);
131 var->
cwnd = (UINT16)newcwnd;
135static UINT32 get_cwnd(PNETSIM_SOCKET s)
137 PCONGESTIONVAR var = get_congestionvar(s);
138 UINT32 c = (UINT32)var->
cwnd;
139 if (s->tcb->isWindowScaling)
141 return c << s->tcb->Snd.Wind_Shift;
149static void set_ssthres(PNETSIM_SOCKET s, UINT32 newssthres)
151 PCONGESTIONVAR var = get_congestionvar(s);
152 if (s->tcb->isWindowScaling)
154 UINT32 c = (UINT32)newssthres;
155 var->
ssthresh = (UINT16)(c >> s->tcb->Snd.Wind_Shift);
163static UINT32 get_ssthres(PNETSIM_SOCKET s)
165 PCONGESTIONVAR var = get_congestionvar(s);
167 if (s->tcb->isWindowScaling)
169 return c << s->tcb->Snd.Wind_Shift;
177static void increase_cwnd(PNETSIM_SOCKET s, UINT16 increase)
179 PCONGESTIONVAR var = get_congestionvar(s);
180 if (s->tcb->isWindowScaling)
182 UINT32 c = window_scale_get_cwnd(s);
184 var->
cwnd = (UINT16)(c >> s->tcb->Snd.Wind_Shift);
185 assert(var->
cwnd < 65535);
189 UINT32 c = var->
cwnd + increase;
197static inline bool tcp_in_slow_start(PNETSIM_SOCKET s)
199 return (get_cwnd(s) <= get_ssthres(s));
202static bool isDupAck(PNETSIM_SOCKET s, PCONGESTIONVAR var)
205 return (s->tcb->SEG.ACK <= s->tcb->SND.UNA);
211static inline UINT64 div64_u64(UINT64 dividend, UINT64 divisor)
213 return dividend / divisor;
216#define do_div(x,y) (x=x/y)
218static inline bool before(UINT32 seq1, UINT32 seq2)
220 return (
int)(seq1 - seq2) < 0;
222#define after(seq2, seq1) before(seq1, seq2)
224static UINT count_bit(UINT64 n)
229 int len = (int)strlen(a);
230 for (i = 0; i < len; i++)
236static void slowStart(PNETSIM_SOCKET s, PCONGESTIONVAR var)
242 N = (UINT16)(s->tcb->SEG.ACK - s->tcb->SND.UNA);
243 increase_cwnd(s, min(N, var->
SMSS));
246static inline void cubictcp_reset(PCUBIC ca)
249 ca->last_max_cwnd = 0;
252 ca->bic_origin_point = 0;
261static inline UINT32 cubictcp_clock(
void)
263 return (UINT32)(pstruEventDetails->dEventTime / 1000);
266static inline void cubictcp_hystart_reset(PNETSIM_SOCKET sk)
268 PCUBIC ca = &get_congestionvar(sk)->cubic;
270 ca->round_start = ca->last_ack = cubictcp_clock();
271 ca->end_seq = sk->tcb->SND.NXT;
276static void cubictcp_init(PNETSIM_SOCKET sk, PTCP_DEV_VAR tcp)
278 PCUBIC ca = &get_congestionvar(sk)->cubic;
283 ca->hystart_ack_delta = tcp->hystart_ack_delta;
284 ca->hystart_low_window = tcp->hystart_low_window;
285 ca->beta = (int)tcp->beta;
286 ca->bic_scale = tcp->bic_scale;
287 ca->initial_ssthresh = tcp->initSSThresh;
290 cubictcp_hystart_reset(sk);
293void init_cubic(PNETSIM_SOCKET s)
295 PTCP_DEV_VAR tcp = GET_TCP_DEV_VAR(s->localDeviceId);
296 PCONGESTIONVAR var = get_congestionvar(s);
299 var = (PCONGESTIONVAR)calloc(1,
sizeof* var);
300 set_congestionvar(s, var);
302 var->
SMSS = tcp->MSS;
303 var->
RMSS = tcp->MSS;
304 set_ssthres(s, tcp->initSSThresh);
308 var->isFastRetransmit =
true;
310 cubictcp_init(s,tcp);
311 PCUBIC cubic = &var->cubic;
313 beta_scale = 8 * (BICTCP_BETA_SCALE + cubic->beta) / 3
314 / (BICTCP_BETA_SCALE - cubic->beta);
316 cube_rtt_scale = (cubic->bic_scale * 10);
333#pragma warning(disable:4310)
334 cube_factor = (UINT32)(1ull << (10 + 3 * BICTCP_HZ));
335#pragma warning(default:4310)
338 do_div(cube_factor, cubic->bic_scale * 10);
341#ifdef _TEST_CONGESTION_
342 fp = fopen(
"Congestion.csv",
"w");
343 fprintf(fp,
"Called For,Time,CWND,ssThres,Flight Size,Ackno,UNA,\n");
352static UINT32 cubic_root(UINT64 a)
363 static const UINT8 v[] = {
364 0, 54, 54, 54, 118, 118, 118, 118,
365 123, 129, 134, 138, 143, 147, 151, 156,
366 157, 161, 164, 168, 170, 173, 176, 179,
367 181, 185, 187, 190, 192, 194, 197, 199,
368 200, 202, 204, 206, 209, 211, 213, 215,
369 217, 219, 221, 222, 224, 225, 227, 229,
370 231, 232, 234, 236, 237, 239, 240, 242,
371 244, 245, 246, 248, 250, 251, 252, 254,
377 return ((UINT32)v[(UINT32)a] + 35) >> 6;
380 b = ((b * 84) >> 8) - 1;
381 shift = (UINT32)(a >> (b * 3));
383 x = ((UINT32)(((UINT32)v[shift] + 10) << b)) >> 6;
391 x = (2 * x + (UINT32)div64_u64(a, (UINT64)x * (UINT64)(x - 1)));
392 x = ((x * 341) >> 10);
396static UINT32 cubictcp_recalc_ssthresh(PNETSIM_SOCKET sk)
398 PCUBIC ca = &get_congestionvar(sk)->cubic;
400 UINT32 segCwnd = (UINT32)get_cwnd(sk) / sk->tcb->get_MSS(sk);
405 if (segCwnd < ca->last_max_cwnd && fast_convergence)
406 ca->last_max_cwnd = (segCwnd * (BICTCP_BETA_SCALE + ca->beta))
407 / (2 * BICTCP_BETA_SCALE);
409 ca->last_max_cwnd = segCwnd;
411 ca->loss_cwnd = segCwnd;
413 return max((segCwnd * ca->beta) / BICTCP_BETA_SCALE, 2U);
419static inline void cubictcp_update(PNETSIM_SOCKET sk, UINT32 acked)
421 PCONGESTIONVAR c = get_congestionvar(sk);
422 PCUBIC ca = &c->cubic;
423 UINT32 cwnd = (UINT32)get_cwnd(sk) / sk->tcb->get_MSS(sk);
425 UINT32 delta, bic_target, max_cnt;
428 ca->ack_cnt += acked;
430 if (ca->last_cwnd == cwnd &&
431 (
int)(tcp_time_stamp - ca->last_time) <= CUBIC_HZ / 32)
438 if (ca->epoch_start && tcp_time_stamp == ca->last_time)
439 goto tcp_friendliness;
441 ca->last_cwnd = cwnd;
442 ca->last_time = tcp_time_stamp;
444 if (ca->epoch_start == 0)
446 ca->epoch_start = tcp_time_stamp;
450 if (ca->last_max_cwnd <= cwnd)
453 ca->bic_origin_point = cwnd;
460 ca->bic_K = cubic_root((UINT64)cube_factor
461 * (ca->last_max_cwnd - cwnd));
462 ca->bic_origin_point = ca->last_max_cwnd;
480 t = (UINT64)(tcp_time_stamp - ca->epoch_start);
481 t += (ca->delay_min >> 3);
487 offs = ca->bic_K - t;
489 offs = t - ca->bic_K;
492 delta = (cube_rtt_scale * offs * offs * offs) >> (10 + 3 * BICTCP_HZ);
494 bic_target = ca->bic_origin_point - delta;
496 bic_target = ca->bic_origin_point + delta;
499 if (bic_target > cwnd)
501 ca->cnt = cwnd / (bic_target - cwnd);
505 ca->cnt = 100 * cwnd;
512 if (ca->last_max_cwnd == 0 && ca->cnt > 20)
517 if (tcp_friendliness)
519 UINT32 scale = beta_scale;
521 delta = (cwnd * scale) >> 3;
522 while (delta && ca->ack_cnt > delta)
524 ca->ack_cnt -= delta;
528 if (ca->tcp_cwnd > cwnd)
530 delta = ca->tcp_cwnd - cwnd;
531 max_cnt = cwnd / delta;
532 if (ca->cnt > max_cnt)
540 ca->cnt = max(ca->cnt, 2U);
544static void cubictcp_cong_avoid(PNETSIM_SOCKET sk, UINT32 segmentAcked)
546 PCONGESTIONVAR var = get_congestionvar(sk);
547 PCUBIC ca = &var->cubic;
548 UINT32 ack = sk->tcb->SEG.ACK;
550 if (tcp_in_slow_start(sk))
552 if (hystart && after(ack, ca->end_seq))
553 cubictcp_hystart_reset(sk);
554 slowStart(sk, get_congestionvar(sk));
558 if (!tcp_in_slow_start(sk) && segmentAcked > 0)
561 ca->cwndcnt += segmentAcked;
562 cubictcp_update(sk,segmentAcked);
563 UINT32 cnt = ca->cnt;
569 if (ca->cwndcnt > cnt)
571 increase_cwnd(sk, var->
SMSS);
581static void hystart_update(PNETSIM_SOCKET sk, UINT32 delay)
583 PCONGESTIONVAR var = get_congestionvar(sk);
584 PCUBIC ca = &var->cubic;
586 if (ca->found & hystart_detect)
589 if (hystart_detect & HYSTART_ACK_TRAIN) {
590 UINT32 now = cubictcp_clock();
593 if ((
int)(now - ca->last_ack) <= ca->hystart_ack_delta)
596 if ((
int)(now - ca->round_start) > ca->delay_min >> 4)
598 ca->found |= HYSTART_ACK_TRAIN;
599 if(get_ssthres(sk) != ca->initial_ssthresh)
600 set_cwnd(sk, get_ssthres(sk));
605 if (hystart_detect & HYSTART_DELAY)
608 if (ca->sample_cnt < HYSTART_MIN_SAMPLES)
610 if (ca->curr_rtt == 0 || ca->curr_rtt > delay)
611 ca->curr_rtt = delay;
617 if (ca->curr_rtt > ca->delay_min +
618 HYSTART_DELAY_THRESH(ca->delay_min >> 3))
620 ca->found |= HYSTART_DELAY;
621 set_ssthres(sk, get_cwnd(sk));
630static void cubictcp_acked(PNETSIM_SOCKET sk)
632 PCUBIC ca = &get_congestionvar(sk)->cubic;
634 UINT32 cwnd = (UINT32)get_cwnd(sk) / sk->tcb->get_MSS(sk);
635 delay = (UINT32)((((UINT32)get_RTT(sk->tcb, sk->tcb->SEG.ACK)) << 3) / MILLISECOND);
640 if (hystart && tcp_in_slow_start(sk) &&
641 cwnd >= ca->hystart_low_window)
642 hystart_update(sk, delay);
645static void cubictcp_fastretransmit(PNETSIM_SOCKET s)
647 PCONGESTIONVAR var = get_congestionvar(s);
649 if (var->dupAckCount == TCP_DupThresh)
651 UINT32 ssThresh = cubictcp_recalc_ssthresh(s);
652 set_ssthres(s, ssThresh*s->tcb->get_MSS(s));
660 set_cwnd(s, get_ssthres(s) + TCP_DupThresh * var->
SMSS);
661 resend_segment_without_timeout(s, s->tcb->SEG.ACK);
663 var->isFastRecovery =
true;
665 if (s->tcb->isSackOption)
667 tcp_sack_fastRetransmit(s);
668 var->isLossRecoveryPhase =
true;
679 increase_cwnd(s, var->
SMSS);
681 if (s->tcb->isSackOption &&
682 var->isLossRecoveryPhase)
684 var->isLossRecoveryPhase = tcp_sack_lossRecoveryPhase(s);
689static void FastRecovery(PNETSIM_SOCKET s, PCONGESTIONVAR var)
691 var->isFastRecovery =
false;
692 var->dupAckCount = 0;
693 set_cwnd(s, get_ssthres(s));
696void cubic_ack_received(PNETSIM_SOCKET s)
698 PCONGESTIONVAR var = get_congestionvar(s);
699 UINT32 segmentAcked = (s->tcb->SEG.ACK - s->tcb->SND.UNA) /
702 bool isdup = isDupAck(s, var);
706 if (var->dupAckCount < TCP_DupThresh)
708 if (s->tcb->isSackOption &&
709 tcp_sack_isLost(s, get_highAck(s) + 1))
711 cubictcp_fastretransmit(s);
716 cubictcp_fastretransmit(s);
719 else if (var->isFastRecovery)
721 FastRecovery(s, var);
726 cubictcp_cong_avoid(s, segmentAcked);
729#ifdef _TEST_CONGESTION_
730 fprintf(fp,
"Ack,%lf,%d,%d,%d,%d,%d\n",
731 pstruEventDetails->dEventTime,
734 s->tcb->SND.NXT - s->tcb->SND.UNA,
735 s->tcb->SEG.ACK, s->tcb->SND.UNA);
UINT16 IW
Size of the sender's congestion window after the three-way handshake is completed.
UINT16 RMSS
To store the size of the largest segment that the receiver is willing to accept.
UINT16 ssthresh
To store slow start threshold value.
UINT16 rwnd
To store the most recently advertised receiver window size value.
UINT16 SMSS
To store the size of the largest segment that the sender can transmit.
UINT16 cwnd
To store Congestion window size value.