summaryrefslogtreecommitdiffstats
path: root/src/vnet/tcp/tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vnet/tcp/tcp.c')
-rw-r--r--src/vnet/tcp/tcp.c22
1 files changed, 16 insertions, 6 deletions
diff --git a/src/vnet/tcp/tcp.c b/src/vnet/tcp/tcp.c
index e0b67a8e5e5..5c554bac5a9 100644
--- a/src/vnet/tcp/tcp.c
+++ b/src/vnet/tcp/tcp.c
@@ -726,15 +726,25 @@ tcp_round_snd_space (tcp_connection_t * tc, u32 snd_space)
u32
tcp_snd_space (tcp_connection_t * tc)
{
- int snd_space;
+ int snd_space, snt_limited;
- /* If we haven't gotten dupacks or if we did and have gotten sacked bytes
- * then we can still send */
- if (PREDICT_TRUE (tcp_in_cong_recovery (tc) == 0
- && (tc->rcv_dupacks == 0
- || tc->sack_sb.last_sacked_bytes)))
+ if (PREDICT_TRUE (tcp_in_cong_recovery (tc) == 0))
{
snd_space = tcp_available_snd_space (tc);
+
+ /* If we haven't gotten dupacks or if we did and have gotten sacked
+ * bytes then we can still send as per Limited Transmit (RFC3042) */
+ if (PREDICT_FALSE (tc->rcv_dupacks != 0
+ && (tcp_opts_sack_permitted (tc)
+ && tc->sack_sb.last_sacked_bytes == 0)))
+ {
+ if (tc->rcv_dupacks == 1 && tc->limited_transmit != tc->snd_nxt)
+ tc->limited_transmit = tc->snd_nxt;
+ ASSERT (seq_leq (tc->limited_transmit, tc->snd_nxt));
+
+ snt_limited = tc->snd_nxt - tc->limited_transmit;
+ snd_space = clib_max (2 * tc->snd_mss - snt_limited, 0);
+ }
return tcp_round_snd_space (tc, snd_space);
}