Fast recovery ssthresh value

Scheffenegger, Richard Richard.Scheffenegger at
Sun Aug 23 19:56:59 UTC 2020

Hi Liang,

In SACK loss recovery, you can recover up to ssthresh (prior cwnd/2 [or 70% in case of cubic]) lost bytes - at least in theory.

In comparison, (New)Reno can only recover one lost packet per window, and then keeps on transmitting new segments (ack + cwnd), even before the receipt of the retransmitted packet is acked.

For historic reasons, the semantic of the variable cwnd is overloaded during loss recovery, and it doesn't "really" indicate cwnd, but rather indicates if/when retransmissions can happen.

In both cases (also the simple one, with only one packet loss), cwnd should be equal (or near equal) to ssthresh by the time loss recovery is finished - but NOT before! While it may appear like slow-start, the value of the cwnd variable really increases by acked_bytes only per ACK (not acked_bytes + SMSS), since the left edge (snd_una) doesn't move right - unlike during slow-start. But numerically, these different phases (slow-start / sack loss-recovery) may appear very similar.

You could check this using the (loadable) SIFTR module, which captures t_flags (indicating if cong/loss recovery is active), ssthresh, cwnd, and other parameters.

That is at least how things are supposed to work; or have you investigated the timing and behavior of SACK loss recovery and found a deviation to RFC3517? Note that FBSD currently has not fully implemented RFC6675 support (which deviates slightly from 3517 under specific circumstances; I have a patch pending to implemente 6675 rescue retransmissions, but haven't tweaked the other aspects of 6675 vs. 3517.

BTW: While freebsd-net is not the wrong DL per se, TCP, UDP, SCTP specific questions can also be posted to freebsd-transport, which is more narrowly focused.

Best regards,

Richard Scheffenegger

-----Original Message-----
From: owner-freebsd-net at <owner-freebsd-net at> On Behalf Of Liang Tian
Sent: Sonntag, 23. August 2020 00:14
To: freebsd-net <freebsd-net at>
Subject: Fast recovery ssthresh value

NetApp Security WARNING: This is an external email. Do not click links or open attachments unless you recognize the sender and know the content is safe.

Hi all,

When 3 dupacks are received and TCP enter fast recovery, if SACK is used, the CWND is set to maxseg:

2593                     if (tp->t_flags & TF_SACK_PERMIT) {
2594                         TCPSTAT_INC(
2595                             tcps_sack_recovery_episode);
2596                         tp->snd_recover = tp->snd_nxt;
2597                         tp->snd_cwnd = maxseg;
2598                         (void) tp->t_fb->tfb_tcp_output(tp);
2599                         goto drop;
2600                     }

Otherwise(SACK is not in use), CWND is set to maxseg before
tcp_output() and then set back to snd_ssthresh+inflation
2601                     tp->snd_nxt = th->th_ack;
2602                     tp->snd_cwnd = maxseg;
2603                     (void) tp->t_fb->tfb_tcp_output(tp);
2604                     KASSERT(tp->snd_limited <= 2,
2605                         ("%s: tp->snd_limited too big",
2606                         __func__));
2607                     tp->snd_cwnd = tp->snd_ssthresh +
2608                          maxseg *
2609                          (tp->t_dupacks - tp->snd_limited);
2610                     if (SEQ_GT(onxt, tp->snd_nxt))
2611                         tp->snd_nxt = onxt;
2612                     goto drop;

I'm wondering in the SACK case, should CWND be set back to ssthresh(which has been slashed in cc_cong_signal() a few lines above) before line 2599, like non-SACK case, instead of doing slow start from maxseg?
I read rfc6675 and a few others, and it looks like that's the case. I appreciate your opinion, again.

freebsd-net at mailing list
To unsubscribe, send any mail to "freebsd-net-unsubscribe at"

More information about the freebsd-transport mailing list