Skip to content

Commit d35d9a6

Browse files
rluboscarlescufi
authored andcommitted
net: tcp: Implement persistent timer for sending ZWP
Instead of sending ZWP from send context, when it is detected that window is full due to zero-window, implement a proper persistent timer, that is scheduled once zero-window is detected. The timer is responsible for sending ZWP to the peer and is canceled once non-zero-window is notified by the peer. Additionally, in case peer reported zero-window, do not trigger retransmission from net_tcp_queue_data(), as it won't be transmitted anyway by the stack. Signed-off-by: Robert Lubos <[email protected]>
1 parent 7eaacda commit d35d9a6

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

subsys/net/ip/tcp.c

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -426,8 +426,9 @@ static int tcp_conn_unref(struct tcp *conn, int status)
426426
tcp_pkt_unref(conn->queue_recv_data);
427427
}
428428

429-
k_work_cancel_delayable(&conn->timewait_timer);
430-
k_work_cancel_delayable(&conn->fin_timer);
429+
(void)k_work_cancel_delayable(&conn->timewait_timer);
430+
(void)k_work_cancel_delayable(&conn->fin_timer);
431+
(void)k_work_cancel_delayable(&conn->persist_timer);
431432

432433
sys_slist_find_and_remove(&tcp_conns, &conn->next);
433434

@@ -1224,6 +1225,23 @@ static void tcp_fin_timeout(struct k_work *work)
12241225
net_context_unref(conn->context);
12251226
}
12261227

1228+
static void tcp_send_zwp(struct k_work *work)
1229+
{
1230+
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
1231+
struct tcp *conn = CONTAINER_OF(dwork, struct tcp, persist_timer);
1232+
1233+
k_mutex_lock(&conn->lock, K_FOREVER);
1234+
1235+
(void)tcp_out_ext(conn, ACK, NULL, conn->seq - 1);
1236+
1237+
if (conn->send_win == 0) {
1238+
(void)k_work_reschedule_for_queue(
1239+
&tcp_work_q, &conn->persist_timer, K_MSEC(tcp_rto));
1240+
}
1241+
1242+
k_mutex_unlock(&conn->lock);
1243+
}
1244+
12271245
static void tcp_conn_ref(struct tcp *conn)
12281246
{
12291247
int ref_count = atomic_inc(&conn->ref_count) + 1;
@@ -1290,6 +1308,7 @@ static struct tcp *tcp_conn_alloc(struct net_context *context)
12901308
k_work_init_delayable(&conn->fin_timer, tcp_fin_timeout);
12911309
k_work_init_delayable(&conn->send_data_timer, tcp_resend_data);
12921310
k_work_init_delayable(&conn->recv_queue_timer, tcp_cleanup_recv_queue);
1311+
k_work_init_delayable(&conn->persist_timer, tcp_send_zwp);
12931312

12941313
tcp_conn_ref(conn);
12951314

@@ -1835,6 +1854,13 @@ static void tcp_in(struct tcp *conn, struct net_pkt *pkt)
18351854
conn->send_win = max_win;
18361855
}
18371856

1857+
if (conn->send_win == 0) {
1858+
(void)k_work_reschedule_for_queue(
1859+
&tcp_work_q, &conn->persist_timer, K_MSEC(tcp_rto));
1860+
} else {
1861+
(void)k_work_cancel_delayable(&conn->persist_timer);
1862+
}
1863+
18381864
if (tcp_window_full(conn)) {
18391865
(void)k_sem_take(&conn->tx_sem, K_NO_WAIT);
18401866
} else {
@@ -2256,7 +2282,11 @@ int net_tcp_queue_data(struct net_context *context, struct net_pkt *pkt)
22562282

22572283
if (tcp_window_full(conn)) {
22582284
if (conn->send_win == 0) {
2259-
tcp_out_ext(conn, ACK, NULL, conn->seq - 1);
2285+
/* No point retransmiting if the current TX window size
2286+
* is 0.
2287+
*/
2288+
ret = -EAGAIN;
2289+
goto out;
22602290
}
22612291

22622292
/* Trigger resend if the timer is not active */

subsys/net/ip/tcp_private.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ struct tcp { /* TCP connection */
242242
struct k_work_delayable recv_queue_timer;
243243
struct k_work_delayable send_data_timer;
244244
struct k_work_delayable timewait_timer;
245+
struct k_work_delayable persist_timer;
246+
245247
union {
246248
/* Because FIN and establish timers are never happening
247249
* at the same time, share the timer between them to

0 commit comments

Comments
 (0)