diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index d9fff6696a0fd3..dd8f1f5108e878 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 b125331eec274b..e9e41df99f73ad 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;