Skip to content

Commit aaa7e45

Browse files
Eric Dumazetgregkh
Eric Dumazet
authored andcommitted
tcp: defer SACK compression after DupThresh
[ Upstream commit 86de592 ] Jean-Louis reported a TCP regression and bisected to recent SACK compression. After a loss episode (receiver not able to keep up and dropping packets because its backlog is full), linux TCP stack is sending a single SACK (DUPACK). Sender waits a full RTO timer before recovering losses. While RFC 6675 says in section 5, "Algorithm Details", (2) If DupAcks < DupThresh but IsLost (HighACK + 1) returns true -- indicating at least three segments have arrived above the current cumulative acknowledgment point, which is taken to indicate loss -- go to step (4). ... (4) Invoke fast retransmit and enter loss recovery as follows: there are old TCP stacks not implementing this strategy, and still counting the dupacks before starting fast retransmit. While these stacks probably perform poorly when receivers implement LRO/GRO, we should be a little more gentle to them. This patch makes sure we do not enable SACK compression unless 3 dupacks have been sent since last rcv_nxt update. Ideally we should even rearm the timer to send one or two more DUPACK if no more packets are coming, but that will be work aiming for linux-4.21. Many thanks to Jean-Louis for bisecting the issue, providing packet captures and testing this patch. Fixes: 5d9f426 ("tcp: add SACK compression") Reported-by: Jean-Louis Dupond <[email protected]> Tested-by: Jean-Louis Dupond <[email protected]> Signed-off-by: Eric Dumazet <[email protected]> Acked-by: Neal Cardwell <[email protected]> Signed-off-by: David S. Miller <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent b8e0769 commit aaa7e45

File tree

4 files changed

+17
-6
lines changed

4 files changed

+17
-6
lines changed

include/linux/tcp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ struct tcp_sock {
196196
u32 rcv_tstamp; /* timestamp of last received ACK (for keepalives) */
197197
u32 lsndtime; /* timestamp of last sent data packet (for restart window) */
198198
u32 last_oow_ack_time; /* timestamp of last out-of-window ACK */
199+
u32 compressed_ack_rcv_nxt;
199200

200201
u32 tsoffset; /* timestamp offset */
201202

net/ipv4/tcp_input.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4276,7 +4276,7 @@ static void tcp_sack_new_ofo_skb(struct sock *sk, u32 seq, u32 end_seq)
42764276
* If the sack array is full, forget about the last one.
42774277
*/
42784278
if (this_sack >= TCP_NUM_SACKS) {
4279-
if (tp->compressed_ack)
4279+
if (tp->compressed_ack > TCP_FASTRETRANS_THRESH)
42804280
tcp_send_ack(sk);
42814281
this_sack--;
42824282
tp->rx_opt.num_sacks--;
@@ -5196,7 +5196,17 @@ static void __tcp_ack_snd_check(struct sock *sk, int ofo_possible)
51965196
if (!tcp_is_sack(tp) ||
51975197
tp->compressed_ack >= sock_net(sk)->ipv4.sysctl_tcp_comp_sack_nr)
51985198
goto send_now;
5199-
tp->compressed_ack++;
5199+
5200+
if (tp->compressed_ack_rcv_nxt != tp->rcv_nxt) {
5201+
tp->compressed_ack_rcv_nxt = tp->rcv_nxt;
5202+
if (tp->compressed_ack > TCP_FASTRETRANS_THRESH)
5203+
NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPACKCOMPRESSED,
5204+
tp->compressed_ack - TCP_FASTRETRANS_THRESH);
5205+
tp->compressed_ack = 0;
5206+
}
5207+
5208+
if (++tp->compressed_ack <= TCP_FASTRETRANS_THRESH)
5209+
goto send_now;
52005210

52015211
if (hrtimer_is_queued(&tp->compressed_ack_timer))
52025212
return;

net/ipv4/tcp_output.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,10 @@ static inline void tcp_event_ack_sent(struct sock *sk, unsigned int pkts,
165165
{
166166
struct tcp_sock *tp = tcp_sk(sk);
167167

168-
if (unlikely(tp->compressed_ack)) {
168+
if (unlikely(tp->compressed_ack > TCP_FASTRETRANS_THRESH)) {
169169
NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPACKCOMPRESSED,
170-
tp->compressed_ack);
171-
tp->compressed_ack = 0;
170+
tp->compressed_ack - TCP_FASTRETRANS_THRESH);
171+
tp->compressed_ack = TCP_FASTRETRANS_THRESH;
172172
if (hrtimer_try_to_cancel(&tp->compressed_ack_timer) == 1)
173173
__sock_put(sk);
174174
}

net/ipv4/tcp_timer.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,7 @@ static enum hrtimer_restart tcp_compressed_ack_kick(struct hrtimer *timer)
740740

741741
bh_lock_sock(sk);
742742
if (!sock_owned_by_user(sk)) {
743-
if (tp->compressed_ack)
743+
if (tp->compressed_ack > TCP_FASTRETRANS_THRESH)
744744
tcp_send_ack(sk);
745745
} else {
746746
if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED,

0 commit comments

Comments
 (0)