From 349bf81e00ffc41a667330ca8b1c3eacf763d108 Mon Sep 17 00:00:00 2001 From: Robert Lubos Date: Mon, 29 Jul 2024 12:01:50 +0200 Subject: [PATCH] net: tcp: Keep track of recv window size change since last ACK Windows TCP stack has a peculiar behavior - when running iperf, it will fill out the RX window almost entirely, but will not set PSH flag on packets. In result, our stack would delay the ACK and thus window update, affecting throughputs heavily. In order to avoid that, keep track of the most recent window size reported to the peer, and reduce it when receiving new data. In case the RX window, as seen from the peer perspective, drops below certain threshold, and the real RX window is currently empty, send an ACK immediately when updating window, so that peer can continue with sending data. Signed-off-by: Robert Lubos --- subsys/net/ip/tcp.c | 24 +++++++++++++++++++++++- subsys/net/ip/tcp_private.h | 1 + 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index d9fff6696a0..dd8f1f5108e 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -1177,6 +1177,17 @@ static bool tcp_short_window(struct tcp *conn) return true; } +static bool tcp_need_window_update(struct tcp *conn) +{ + int32_t threshold = MAX(conn_mss(conn), conn->recv_win_max / 2); + + /* In case window is full again, and we didn't send a window update + * since the window size dropped below threshold, do it now. + */ + return (conn->recv_win == conn->recv_win_max && + conn->recv_win_sent <= threshold); +} + /** * @brief Update TCP receive window * @@ -1205,7 +1216,8 @@ static int tcp_update_recv_wnd(struct tcp *conn, int32_t delta) short_win_after = tcp_short_window(conn); - if (short_win_before && !short_win_after && + if (((short_win_before && !short_win_after) || + tcp_need_window_update(conn)) && conn->state == TCP_ESTABLISHED) { k_work_cancel_delayable(&conn->ack_timer); tcp_out(conn, ACK); @@ -1297,6 +1309,11 @@ static enum net_verdict tcp_data_get(struct tcp *conn, struct net_pkt *pkt, size net_pkt_skip(pkt, net_pkt_get_len(pkt) - *len); tcp_update_recv_wnd(conn, -*len); + if (*len > conn->recv_win_sent) { + conn->recv_win_sent = 0; + } else { + conn->recv_win_sent -= *len; + } /* Do not pass data to application with TCP conn * locked as there could be an issue when the app tries @@ -1583,6 +1600,10 @@ static int tcp_out_ext(struct tcp *conn, uint8_t flags, struct net_pkt *data, sys_slist_append(&conn->send_queue, &pkt->next); + if (flags & ACK) { + conn->recv_win_sent = conn->recv_win; + } + if (is_destination_local(pkt)) { /* If the destination is local, we have to let the current * thread to finish with any state-machine changes before @@ -2112,6 +2133,7 @@ static struct tcp *tcp_conn_alloc(void) conn->state = TCP_LISTEN; conn->recv_win_max = tcp_rx_window; conn->recv_win = conn->recv_win_max; + conn->recv_win_sent = conn->recv_win_max; conn->send_win_max = MAX(tcp_tx_window, NET_IPV6_MTU); conn->send_win = conn->send_win_max; conn->tcp_nodelay = false; diff --git a/subsys/net/ip/tcp_private.h b/subsys/net/ip/tcp_private.h index b125331eec2..e9e41df99f7 100644 --- a/subsys/net/ip/tcp_private.h +++ b/subsys/net/ip/tcp_private.h @@ -314,6 +314,7 @@ struct tcp { /* TCP connection */ uint32_t keep_cnt; uint32_t keep_cur; #endif /* CONFIG_NET_TCP_KEEPALIVE */ + uint16_t recv_win_sent; uint16_t recv_win_max; uint16_t recv_win; uint16_t send_win_max;