From: Eric Dumazet <edumazet@google.com> Date: 2018-05-17 12:12:21
When TCP receives an out-of-order packet, it immediately sends
a SACK packet, generating network load but also forcing the
receiver to send 1-MSS pathological packets, increasing its
RTX queue length/depth, and thus processing time.
Wifi networks suffer from this aggressive behavior, but generally
speaking, all these SACK packets add fuel to the fire when networks
are under congestion.
This patch series adds SACK compression, but the infrastructure
could be leveraged to also compress ACK in the future.
Eric Dumazet (4):
tcp: use __sock_put() instead of sock_put() in tcp_clear_xmit_timers()
tcp: do not force quickack when receiving out-of-order packets
tcp: add SACK compression
tcp: add TCPAckCompressed SNMP counter
include/linux/tcp.h | 2 ++
include/net/tcp.h | 5 ++++-
include/uapi/linux/snmp.h | 1 +
net/ipv4/proc.c | 1 +
net/ipv4/tcp.c | 1 +
net/ipv4/tcp_input.c | 33 +++++++++++++++++++++++++--------
net/ipv4/tcp_output.c | 9 +++++++++
net/ipv4/tcp_timer.c | 25 +++++++++++++++++++++++++
8 files changed, 68 insertions(+), 9 deletions(-)
--
2.17.0.441.gb46fe60e1d-goog
From: Eric Dumazet <edumazet@google.com> Date: 2018-05-17 12:12:27
As explained in commit 9f9843a751d0 ("tcp: properly handle stretch
acks in slow start"), TCP stacks have to consider how many packets
are acknowledged in one single ACK, because of GRO, but also
because of ACK compression or losses.
We plan to add SACK compression in the following patch, we
must therefore not call tcp_enter_quickack_mode()
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
net/ipv4/tcp_input.c | 2 --
1 file changed, 2 deletions(-)
On Thu, May 17, 2018 at 8:12 AM Eric Dumazet [off-list ref] wrote:
As explained in commit 9f9843a751d0 ("tcp: properly handle stretch
acks in slow start"), TCP stacks have to consider how many packets
are acknowledged in one single ACK, because of GRO, but also
because of ACK compression or losses.
We plan to add SACK compression in the following patch, we
must therefore not call tcp_enter_quickack_mode()
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
On Thu, May 17, 2018 at 8:12 AM, Eric Dumazet [off-list ref] wrote:
As explained in commit 9f9843a751d0 ("tcp: properly handle stretch
acks in slow start"), TCP stacks have to consider how many packets
are acknowledged in one single ACK, because of GRO, but also
because of ACK compression or losses.
We plan to add SACK compression in the following patch, we
must therefore not call tcp_enter_quickack_mode()
Signed-off-by: Eric Dumazet <edumazet@google.com>
From: Eric Dumazet <edumazet@google.com> Date: 2018-05-17 12:12:30
When TCP receives an out-of-order packet, it immediately sends
a SACK packet, generating network load but also forcing the
receiver to send 1-MSS pathological packets, increasing its
RTX queue length/depth, and thus processing time.
Wifi networks suffer from this aggressive behavior, but generally
speaking, all these SACK packets add fuel to the fire when networks
are under congestion.
This patch adds a high resolution timer and tp->compressed_ack counter.
Instead of sending a SACK, we program this timer with a small delay,
based on SRTT and capped to 2.5 ms : delay = min ( 5 % of SRTT, 2.5 ms)
If subsequent SACKs need to be sent while the timer has not yet expired,
we simply increment tp->compressed_ack
When timer expires, a SACK is sent with the latest information.
Note that tcp_sack_new_ofo_skb() is able to force a SACK to be sent
if the sack blocks need to be shuffled, even if the timer has not
expired.
A new SNMP counter is added in the following patch.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
include/linux/tcp.h | 2 ++
include/net/tcp.h | 3 +++
net/ipv4/tcp.c | 1 +
net/ipv4/tcp_input.c | 31 +++++++++++++++++++++++++------
net/ipv4/tcp_output.c | 7 +++++++
net/ipv4/tcp_timer.c | 25 +++++++++++++++++++++++++
6 files changed, 63 insertions(+), 6 deletions(-)
@@ -218,6 +218,7 @@ struct tcp_sock {reord:1;/* reordering detected */}rack;u16advmss;/* Advertised MSS */+u8compressed_ack;u32chrono_start;/* Start time in jiffies of a TCP chrono */u32chrono_stat[3];/* Time in jiffies for chrono_stat stats */u8chrono_type:2,/* current chronograph type */
@@ -2595,6 +2595,7 @@ int tcp_disconnect(struct sock *sk, int flags)dst_release(sk->sk_rx_dst);sk->sk_rx_dst=NULL;tcp_saved_syn_free(tp);+tp->compressed_ack=0;/* Clean up fastopen related fields */tcp_free_fastopen_req(tp);
@@ -5074,6 +5076,7 @@ static inline void tcp_data_snd_check(struct sock *sk)staticvoid__tcp_ack_snd_check(structsock*sk,intofo_possible){structtcp_sock*tp=tcp_sk(sk);+unsignedlongdelay;/* More than one full frame received... */if(((tp->rcv_nxt-tp->rcv_wup)>inet_csk(sk)->icsk_ack.rcv_mss&&
@@ -5085,15 +5088,31 @@ static void __tcp_ack_snd_check(struct sock *sk, int ofo_possible)(tp->rcv_nxt-tp->copied_seq<sk->sk_rcvlowat||__tcp_select_window(sk)>=tp->rcv_wnd))||/* We ACK each frame or... */-tcp_in_quickack_mode(sk)||-/* We have out of order data. */-(ofo_possible&&!RB_EMPTY_ROOT(&tp->out_of_order_queue))){-/* Then ack it now */+tcp_in_quickack_mode(sk)){+send_now:tcp_send_ack(sk);-}else{-/* Else, send delayed ack. */+return;+}++if(!ofo_possible||RB_EMPTY_ROOT(&tp->out_of_order_queue)){tcp_send_delayed_ack(sk);+return;}++if(!tcp_is_sack(tp)||tp->compressed_ack>=127)+gotosend_now;+tp->compressed_ack++;++if(hrtimer_is_queued(&tp->compressed_ack_timer))+return;++/* compress ack timer : 5 % of srtt, but no more than 2.5 ms */++delay=min_t(unsignedlong,2500*NSEC_PER_USEC,+tp->rcv_rtt_est.rtt_us*(NSEC_PER_USEC>>3)/20);+sock_hold(sk);+hrtimer_start(&tp->compressed_ack_timer,ns_to_ktime(delay),+HRTIMER_MODE_REL_PINNED_SOFT);}staticinlinevoidtcp_ack_snd_check(structsock*sk)
On Thu, May 17, 2018 at 8:12 AM Eric Dumazet [off-list ref] wrote:
When TCP receives an out-of-order packet, it immediately sends
a SACK packet, generating network load but also forcing the
receiver to send 1-MSS pathological packets, increasing its
RTX queue length/depth, and thus processing time.
Wifi networks suffer from this aggressive behavior, but generally
speaking, all these SACK packets add fuel to the fire when networks
are under congestion.
This patch adds a high resolution timer and tp->compressed_ack counter.
Instead of sending a SACK, we program this timer with a small delay,
based on SRTT and capped to 2.5 ms : delay = min ( 5 % of SRTT, 2.5 ms)
...
Very nice. Thanks for implementing this, Eric! I was wondering if the
constants here might be worth some discussion/elaboration.
(tp->rcv_nxt - tp->copied_seq < sk->sk_rcvlowat ||
__tcp_select_window(sk) >= tp->rcv_wnd)) ||
/* We ACK each frame or... */
- tcp_in_quickack_mode(sk) ||
- /* We have out of order data. */
- (ofo_possible && !RB_EMPTY_ROOT(&tp->out_of_order_queue))) {
- /* Then ack it now */
+ tcp_in_quickack_mode(sk)) {
+send_now:
tcp_send_ack(sk);
- } else {
- /* Else, send delayed ack. */
+ return;
+ }
+
+ if (!ofo_possible || RB_EMPTY_ROOT(&tp->out_of_order_queue)) {
tcp_send_delayed_ack(sk);
+ return;
}
+
+ if (!tcp_is_sack(tp) || tp->compressed_ack >= 127)
+ goto send_now;
+ tp->compressed_ack++;
Is there a particular motivation for the cap of 127? IMHO 127 ACKs is quite
a few to compress. Experience seems to show that it works well to have one
GRO ACK for ~64KBytes that triggers a single TSO skb of ~64KBytes. It might
be nice to try to match those dynamics in this SACK compression case, so it
might be nice to cap the number of compressed ACKs at something like 44?
(0xffff / 1448 - 1). That way for high-speed paths we could try to keep
the ACK clock going with ACKs for ~64KBytes that trigger a single TSO skb
of ~64KBytes, no matter whether we are sending SACKs or cumulative ACKs.
+
+ if (hrtimer_is_queued(&tp->compressed_ack_timer))
+ return;
+
+ /* compress ack timer : 5 % of srtt, but no more than 2.5 ms */
+
+ delay = min_t(unsigned long, 2500 * NSEC_PER_USEC,
+ tp->rcv_rtt_est.rtt_us * (NSEC_PER_USEC >> 3)/20);
Any particular motivation for the 2.5ms here? It might be nice to match the
existing TSO autosizing dynamics and use 1ms here instead of having a
separate new constant of 2.5ms. Smaller time scales here should lead to
less burstiness and queue pressure from data packets in the network, and we
know from experience that the CPU overhead of 1ms chunks is acceptable.
thanks,
neal
From: Eric Dumazet <hidden> Date: 2018-05-17 15:40:32
On 05/17/2018 08:14 AM, Neal Cardwell wrote:
On Thu, May 17, 2018 at 8:12 AM Eric Dumazet [off-list ref] wrote:
quoted
When TCP receives an out-of-order packet, it immediately sends
a SACK packet, generating network load but also forcing the
receiver to send 1-MSS pathological packets, increasing its
RTX queue length/depth, and thus processing time.
quoted
Wifi networks suffer from this aggressive behavior, but generally
speaking, all these SACK packets add fuel to the fire when networks
are under congestion.
quoted
This patch adds a high resolution timer and tp->compressed_ack counter.
quoted
Instead of sending a SACK, we program this timer with a small delay,
based on SRTT and capped to 2.5 ms : delay = min ( 5 % of SRTT, 2.5 ms)
...
Very nice. Thanks for implementing this, Eric! I was wondering if the
constants here might be worth some discussion/elaboration.
(tp->rcv_nxt - tp->copied_seq < sk->sk_rcvlowat ||
__tcp_select_window(sk) >= tp->rcv_wnd)) ||
/* We ACK each frame or... */
- tcp_in_quickack_mode(sk) ||
- /* We have out of order data. */
- (ofo_possible && !RB_EMPTY_ROOT(&tp->out_of_order_queue))) {
- /* Then ack it now */
+ tcp_in_quickack_mode(sk)) {
+send_now:
tcp_send_ack(sk);
- } else {
- /* Else, send delayed ack. */
+ return;
+ }
+
+ if (!ofo_possible || RB_EMPTY_ROOT(&tp->out_of_order_queue)) {
tcp_send_delayed_ack(sk);
+ return;
}
+
+ if (!tcp_is_sack(tp) || tp->compressed_ack >= 127)
+ goto send_now;
+ tp->compressed_ack++;
Is there a particular motivation for the cap of 127? IMHO 127 ACKs is quite
a few to compress. Experience seems to show that it works well to have one
GRO ACK for ~64KBytes that triggers a single TSO skb of ~64KBytes. It might
be nice to try to match those dynamics in this SACK compression case, so it
might be nice to cap the number of compressed ACKs at something like 44?
(0xffff / 1448 - 1). That way for high-speed paths we could try to keep
the ACK clock going with ACKs for ~64KBytes that trigger a single TSO skb
of ~64KBytes, no matter whether we are sending SACKs or cumulative ACKs.
127 was chosen because the field is u8, and since skb allocation for the ACK
can fail, we could have cases were the field goes above 127.
Ultimately, I believe a followup patch would add a sysctl, so that we can fine-tune
this, and eventually disable ACK compression if this sysctl is set to 0
quoted
+
+ if (hrtimer_is_queued(&tp->compressed_ack_timer))
+ return;
+
+ /* compress ack timer : 5 % of srtt, but no more than 2.5 ms */
+
+ delay = min_t(unsigned long, 2500 * NSEC_PER_USEC,
+ tp->rcv_rtt_est.rtt_us * (NSEC_PER_USEC >> 3)/20);
Any particular motivation for the 2.5ms here? It might be nice to match the
existing TSO autosizing dynamics and use 1ms here instead of having a
separate new constant of 2.5ms. Smaller time scales here should lead to
less burstiness and queue pressure from data packets in the network, and we
know from experience that the CPU overhead of 1ms chunks is acceptable.
This came from my tests on wifi really :)
I also had the idea to make this threshold adjustable for wifi, like we did for sk_pacing_shift.
(On wifi, we might want to increase the max delay between ACK)
So maybe use 1ms delay, when sk_pacing_shift == 10, but increase it if sk_pacing_shift has been lowered.
From: Eric Dumazet <hidden> Date: 2018-05-17 15:46:28
On 05/17/2018 08:40 AM, Eric Dumazet wrote:
On 05/17/2018 08:14 AM, Neal Cardwell wrote:
quoted
Any particular motivation for the 2.5ms here? It might be nice to match the
existing TSO autosizing dynamics and use 1ms here instead of having a
separate new constant of 2.5ms. Smaller time scales here should lead to
less burstiness and queue pressure from data packets in the network, and we
know from experience that the CPU overhead of 1ms chunks is acceptable.
This came from my tests on wifi really :)
I also had the idea to make this threshold adjustable for wifi, like we did for sk_pacing_shift.
(On wifi, we might want to increase the max delay between ACK)
So maybe use 1ms delay, when sk_pacing_shift == 10, but increase it if sk_pacing_shift has been lowered.
BTW, maybe my changelog or patch is not clear enough :
As soon as some packets are received in order, we send an ACK, even if the timer was armed.
(This is the beginning of __tcp_ack_snd_check())
When this ACK is sent, timer is canceled (in tcp_event_ack_sent())
On Thu, May 17, 2018 at 11:40 AM Eric Dumazet [off-list ref]
wrote:
On 05/17/2018 08:14 AM, Neal Cardwell wrote:
quoted
Is there a particular motivation for the cap of 127? IMHO 127 ACKs is
quite
quoted
a few to compress. Experience seems to show that it works well to have
one
quoted
GRO ACK for ~64KBytes that triggers a single TSO skb of ~64KBytes. It
might
quoted
be nice to try to match those dynamics in this SACK compression case,
so it
quoted
might be nice to cap the number of compressed ACKs at something like 44?
(0xffff / 1448 - 1). That way for high-speed paths we could try to keep
the ACK clock going with ACKs for ~64KBytes that trigger a single TSO
skb
quoted
of ~64KBytes, no matter whether we are sending SACKs or cumulative ACKs.
127 was chosen because the field is u8, and since skb allocation for the
ACK
can fail, we could have cases were the field goes above 127.
Ultimately, I believe a followup patch would add a sysctl, so that we can
fine-tune
this, and eventually disable ACK compression if this sysctl is set to 0
OK, a sysctl sounds good. I would still vote for a default of 44. :-)
quoted
quoted
+ if (hrtimer_is_queued(&tp->compressed_ack_timer))
+ return;
+
+ /* compress ack timer : 5 % of srtt, but no more than 2.5 ms */
+
+ delay = min_t(unsigned long, 2500 * NSEC_PER_USEC,
+ tp->rcv_rtt_est.rtt_us * (NSEC_PER_USEC >>
3)/20);
quoted
Any particular motivation for the 2.5ms here? It might be nice to match
the
quoted
existing TSO autosizing dynamics and use 1ms here instead of having a
separate new constant of 2.5ms. Smaller time scales here should lead to
less burstiness and queue pressure from data packets in the network,
and we
quoted
know from experience that the CPU overhead of 1ms chunks is acceptable.
This came from my tests on wifi really :)
I also had the idea to make this threshold adjustable for wifi, like we
did for sk_pacing_shift.
(On wifi, we might want to increase the max delay between ACK)
So maybe use 1ms delay, when sk_pacing_shift == 10, but increase it if
sk_pacing_shift has been lowered.
Sounds good to me.
Thanks for implementing this! Overall this patch seems nice to me.
Acked-by: Neal Cardwell <ncardwell@google.com>
BTW, I guess we should spread the word to maintainers of other major TCP
stacks that they need to be prepared for what may be a much higher degree
of compression/aggregation in the SACK stream. Linux stacks going back many
years should be fine with this, but I'm not sure about the other major OSes
(they may only allow sending one MSS per ACK-with-SACKs received).
neal
On Thu, May 17, 2018 at 9:41 AM, Neal Cardwell [off-list ref] wrote:
On Thu, May 17, 2018 at 11:40 AM Eric Dumazet [off-list ref]
wrote:
quoted
On 05/17/2018 08:14 AM, Neal Cardwell wrote:
quoted
Is there a particular motivation for the cap of 127? IMHO 127 ACKs is
quite
quoted
quoted
a few to compress. Experience seems to show that it works well to have
one
quoted
quoted
GRO ACK for ~64KBytes that triggers a single TSO skb of ~64KBytes. It
might
quoted
quoted
be nice to try to match those dynamics in this SACK compression case,
so it
quoted
quoted
might be nice to cap the number of compressed ACKs at something like 44?
(0xffff / 1448 - 1). That way for high-speed paths we could try to keep
the ACK clock going with ACKs for ~64KBytes that trigger a single TSO
skb
quoted
quoted
of ~64KBytes, no matter whether we are sending SACKs or cumulative ACKs.
quoted
127 was chosen because the field is u8, and since skb allocation for the
ACK
quoted
can fail, we could have cases were the field goes above 127.
quoted
Ultimately, I believe a followup patch would add a sysctl, so that we can
fine-tune
quoted
this, and eventually disable ACK compression if this sysctl is set to 0
OK, a sysctl sounds good. I would still vote for a default of 44. :-)
quoted
quoted
quoted
+ if (hrtimer_is_queued(&tp->compressed_ack_timer))
+ return;
+
+ /* compress ack timer : 5 % of srtt, but no more than 2.5 ms */
+
+ delay = min_t(unsigned long, 2500 * NSEC_PER_USEC,
+ tp->rcv_rtt_est.rtt_us * (NSEC_PER_USEC >>
3)/20);
quoted
quoted
Any particular motivation for the 2.5ms here? It might be nice to match
the
quoted
quoted
existing TSO autosizing dynamics and use 1ms here instead of having a
separate new constant of 2.5ms. Smaller time scales here should lead to
less burstiness and queue pressure from data packets in the network,
and we
quoted
quoted
know from experience that the CPU overhead of 1ms chunks is acceptable.
quoted
This came from my tests on wifi really :)
quoted
I also had the idea to make this threshold adjustable for wifi, like we
did for sk_pacing_shift.
quoted
(On wifi, we might want to increase the max delay between ACK)
quoted
So maybe use 1ms delay, when sk_pacing_shift == 10, but increase it if
sk_pacing_shift has been lowered.
Sounds good to me.
Thanks for implementing this! Overall this patch seems nice to me.
Acked-by: Neal Cardwell <ncardwell@google.com>
BTW, I guess we should spread the word to maintainers of other major TCP
stacks that they need to be prepared for what may be a much higher degree
of compression/aggregation in the SACK stream. Linux stacks going back many
years should be fine with this, but I'm not sure about the other major OSes
(they may only allow sending one MSS per ACK-with-SACKs received).
Patch looks really good but Neal's comment just reminds me a potential
legacy issue.
I recall at least Apple and Windows TCP stacks still need 3+ DUPACKs
(!= a SACK covering 3+ packets) to trigger fast recovery. Will we have
an issue there interacting w/ these stacks?
From: Eric Dumazet <edumazet@google.com> Date: 2018-05-17 17:15:30
On Thu, May 17, 2018 at 9:59 AM Yuchung Cheng [off-list ref] wrote:
quoted
Thanks for implementing this! Overall this patch seems nice to me.
Acked-by: Neal Cardwell <ncardwell@google.com>
BTW, I guess we should spread the word to maintainers of other major TCP
stacks that they need to be prepared for what may be a much higher
degree
quoted
of compression/aggregation in the SACK stream. Linux stacks going back
many
quoted
years should be fine with this, but I'm not sure about the other major
OSes
quoted
(they may only allow sending one MSS per ACK-with-SACKs received).
Patch looks really good but Neal's comment just reminds me a potential
legacy issue.
I recall at least Apple and Windows TCP stacks still need 3+ DUPACKs
(!= a SACK covering 3+ packets) to trigger fast recovery. Will we have
an issue there interacting w/ these stacks?
Then we should revert GRO :)
Really it is time for these stacks to catch up, or give up to QUIC :/
On Thu, May 17, 2018 at 9:59 AM, Yuchung Cheng [off-list ref] wrote:
On Thu, May 17, 2018 at 9:41 AM, Neal Cardwell [off-list ref] wrote:
quoted
On Thu, May 17, 2018 at 11:40 AM Eric Dumazet [off-list ref]
wrote:
quoted
On 05/17/2018 08:14 AM, Neal Cardwell wrote:
quoted
Is there a particular motivation for the cap of 127? IMHO 127 ACKs is
quite
quoted
quoted
a few to compress. Experience seems to show that it works well to have
one
quoted
quoted
GRO ACK for ~64KBytes that triggers a single TSO skb of ~64KBytes. It
might
quoted
quoted
be nice to try to match those dynamics in this SACK compression case,
so it
quoted
quoted
might be nice to cap the number of compressed ACKs at something like 44?
(0xffff / 1448 - 1). That way for high-speed paths we could try to keep
the ACK clock going with ACKs for ~64KBytes that trigger a single TSO
skb
quoted
quoted
of ~64KBytes, no matter whether we are sending SACKs or cumulative ACKs.
quoted
127 was chosen because the field is u8, and since skb allocation for the
ACK
quoted
can fail, we could have cases were the field goes above 127.
quoted
Ultimately, I believe a followup patch would add a sysctl, so that we can
fine-tune
quoted
this, and eventually disable ACK compression if this sysctl is set to 0
OK, a sysctl sounds good. I would still vote for a default of 44. :-)
quoted
quoted
quoted
+ if (hrtimer_is_queued(&tp->compressed_ack_timer))
+ return;
+
+ /* compress ack timer : 5 % of srtt, but no more than 2.5 ms */
+
+ delay = min_t(unsigned long, 2500 * NSEC_PER_USEC,
+ tp->rcv_rtt_est.rtt_us * (NSEC_PER_USEC >>
3)/20);
quoted
quoted
Any particular motivation for the 2.5ms here? It might be nice to match
the
quoted
quoted
existing TSO autosizing dynamics and use 1ms here instead of having a
separate new constant of 2.5ms. Smaller time scales here should lead to
less burstiness and queue pressure from data packets in the network,
and we
quoted
quoted
know from experience that the CPU overhead of 1ms chunks is acceptable.
quoted
This came from my tests on wifi really :)
quoted
I also had the idea to make this threshold adjustable for wifi, like we
did for sk_pacing_shift.
quoted
(On wifi, we might want to increase the max delay between ACK)
quoted
So maybe use 1ms delay, when sk_pacing_shift == 10, but increase it if
sk_pacing_shift has been lowered.
Sounds good to me.
Thanks for implementing this! Overall this patch seems nice to me.
Acked-by: Neal Cardwell <ncardwell@google.com>
BTW, I guess we should spread the word to maintainers of other major TCP
stacks that they need to be prepared for what may be a much higher degree
of compression/aggregation in the SACK stream. Linux stacks going back many
years should be fine with this, but I'm not sure about the other major OSes
(they may only allow sending one MSS per ACK-with-SACKs received).
Patch looks really good but Neal's comment just reminds me a potential
legacy issue.
I recall at least Apple and Windows TCP stacks still need 3+ DUPACKs
(!= a SACK covering 3+ packets) to trigger fast recovery. Will we have
an issue there interacting w/ these stacks?
Offline chat w/ Eric: actually the problem already exists with GRO: a
Linux receiver could receive a OOO skb of say 5 pkts and returns one
(DUP)ACK w/ sack option covering 5 pkts.
Since no issues have been reported my concern is probably not big
deal. Hopefully other stacks can improve their sack / recovery
handling there.