Commit d9d51301 authored by Yuchung Cheng's avatar Yuchung Cheng Committed by Kleber Sacilotto de Souza

tcp: do not delay ACK in DCTCP upon CE status change

BugLink: https://bugs.launchpad.net/bugs/1791942

[ Upstream commit a0496ef2 ]

Per DCTCP RFC8257 (Section 3.2) the ACK reflecting the CE status change
has to be sent immediately so the sender can respond quickly:

""" When receiving packets, the CE codepoint MUST be processed as follows:

   1.  If the CE codepoint is set and DCTCP.CE is false, set DCTCP.CE to
       true and send an immediate ACK.

   2.  If the CE codepoint is not set and DCTCP.CE is true, set DCTCP.CE
       to false and send an immediate ACK.
"""

Previously DCTCP implementation may continue to delay the ACK. This
patch fixes that to implement the RFC by forcing an immediate ACK.

Tested with this packetdrill script provided by Larry Brakmo

0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
0.000 setsockopt(3, SOL_TCP, TCP_CONGESTION, "dctcp", 5) = 0
0.000 bind(3, ..., ...) = 0
0.000 listen(3, 1) = 0

0.100 < [ect0] SEW 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
0.100 > SE. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 8>
0.110 < [ect0] . 1:1(0) ack 1 win 257
0.200 accept(3, ..., ...) = 4
   +0 setsockopt(4, SOL_SOCKET, SO_DEBUG, [1], 4) = 0

0.200 < [ect0] . 1:1001(1000) ack 1 win 257
0.200 > [ect01] . 1:1(0) ack 1001

0.200 write(4, ..., 1) = 1
0.200 > [ect01] P. 1:2(1) ack 1001

0.200 < [ect0] . 1001:2001(1000) ack 2 win 257
+0.005 < [ce] . 2001:3001(1000) ack 2 win 257

+0.000 > [ect01] . 2:2(0) ack 2001
// Previously the ACK below would be delayed by 40ms
+0.000 > [ect01] E. 2:2(0) ack 3001

+0.500 < F. 9501:9501(0) ack 4 win 257
Signed-off-by: default avatarYuchung Cheng <ycheng@google.com>
Acked-by: default avatarNeal Cardwell <ncardwell@google.com>
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarStefan Bader <stefan.bader@canonical.com>
Signed-off-by: default avatarKleber Sacilotto de Souza <kleber.souza@canonical.com>
parent cd6f83c8
...@@ -376,6 +376,7 @@ ssize_t tcp_splice_read(struct socket *sk, loff_t *ppos, ...@@ -376,6 +376,7 @@ ssize_t tcp_splice_read(struct socket *sk, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len, struct pipe_inode_info *pipe, size_t len,
unsigned int flags); unsigned int flags);
void tcp_enter_quickack_mode(struct sock *sk);
static inline void tcp_dec_quickack_mode(struct sock *sk, static inline void tcp_dec_quickack_mode(struct sock *sk,
const unsigned int pkts) const unsigned int pkts)
{ {
......
...@@ -131,12 +131,15 @@ static void dctcp_ce_state_0_to_1(struct sock *sk) ...@@ -131,12 +131,15 @@ static void dctcp_ce_state_0_to_1(struct sock *sk)
struct dctcp *ca = inet_csk_ca(sk); struct dctcp *ca = inet_csk_ca(sk);
struct tcp_sock *tp = tcp_sk(sk); struct tcp_sock *tp = tcp_sk(sk);
/* State has changed from CE=0 to CE=1 and delayed if (!ca->ce_state) {
* ACK has not sent yet. /* State has changed from CE=0 to CE=1, force an immediate
* ACK to reflect the new CE state. If an ACK was delayed,
* send that first to reflect the prior CE state.
*/ */
if (!ca->ce_state && if (inet_csk(sk)->icsk_ack.pending & ICSK_ACK_TIMER)
inet_csk(sk)->icsk_ack.pending & ICSK_ACK_TIMER)
__tcp_send_ack(sk, ca->prior_rcv_nxt); __tcp_send_ack(sk, ca->prior_rcv_nxt);
tcp_enter_quickack_mode(sk);
}
ca->prior_rcv_nxt = tp->rcv_nxt; ca->prior_rcv_nxt = tp->rcv_nxt;
ca->ce_state = 1; ca->ce_state = 1;
...@@ -149,12 +152,15 @@ static void dctcp_ce_state_1_to_0(struct sock *sk) ...@@ -149,12 +152,15 @@ static void dctcp_ce_state_1_to_0(struct sock *sk)
struct dctcp *ca = inet_csk_ca(sk); struct dctcp *ca = inet_csk_ca(sk);
struct tcp_sock *tp = tcp_sk(sk); struct tcp_sock *tp = tcp_sk(sk);
/* State has changed from CE=1 to CE=0 and delayed if (ca->ce_state) {
* ACK has not sent yet. /* State has changed from CE=1 to CE=0, force an immediate
* ACK to reflect the new CE state. If an ACK was delayed,
* send that first to reflect the prior CE state.
*/ */
if (ca->ce_state && if (inet_csk(sk)->icsk_ack.pending & ICSK_ACK_TIMER)
inet_csk(sk)->icsk_ack.pending & ICSK_ACK_TIMER)
__tcp_send_ack(sk, ca->prior_rcv_nxt); __tcp_send_ack(sk, ca->prior_rcv_nxt);
tcp_enter_quickack_mode(sk);
}
ca->prior_rcv_nxt = tp->rcv_nxt; ca->prior_rcv_nxt = tp->rcv_nxt;
ca->ce_state = 0; ca->ce_state = 0;
......
...@@ -187,13 +187,14 @@ static void tcp_incr_quickack(struct sock *sk) ...@@ -187,13 +187,14 @@ static void tcp_incr_quickack(struct sock *sk)
icsk->icsk_ack.quick = min(quickacks, TCP_MAX_QUICKACKS); icsk->icsk_ack.quick = min(quickacks, TCP_MAX_QUICKACKS);
} }
static void tcp_enter_quickack_mode(struct sock *sk) void tcp_enter_quickack_mode(struct sock *sk)
{ {
struct inet_connection_sock *icsk = inet_csk(sk); struct inet_connection_sock *icsk = inet_csk(sk);
tcp_incr_quickack(sk); tcp_incr_quickack(sk);
icsk->icsk_ack.pingpong = 0; icsk->icsk_ack.pingpong = 0;
icsk->icsk_ack.ato = TCP_ATO_MIN; icsk->icsk_ack.ato = TCP_ATO_MIN;
} }
EXPORT_SYMBOL(tcp_enter_quickack_mode);
/* Send ACKs quickly, if "quick" count is not exhausted /* Send ACKs quickly, if "quick" count is not exhausted
* and the session is not interactive. * and the session is not interactive.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment