net: tcp: Add semaphore indicating if transfer is possible

The semaphore is reset when TCP layer would normally reject transfer
request (either due to TX window being full or entering retransmission
mode). Once data is acnowledged, or the reatransmission is done, the
semaphore is set again.

Upper layers can monitor the semaphore with `k_poll()` instead of
waiting blindly before attempting to transmit again.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
Robert Lubos 2022-05-10 10:17:36 +02:00 committed by Carles Cufí
commit 7eaacdaca6
3 changed files with 41 additions and 0 deletions

View file

@ -1078,6 +1078,10 @@ static int tcp_send_queued_data(struct tcp *conn)
}
}
if (tcp_window_full(conn)) {
(void)k_sem_take(&conn->tx_sem, K_NO_WAIT);
}
if (conn->unacked_len) {
subscribe = true;
}
@ -1140,6 +1144,8 @@ static void tcp_resend_data(struct k_work *work)
conn->data_mode = TCP_DATA_MODE_RESEND;
conn->unacked_len = 0;
(void)k_sem_take(&conn->tx_sem, K_NO_WAIT);
ret = tcp_send_data(conn);
conn->send_data_retries++;
if (ret == 0) {
@ -1163,6 +1169,11 @@ static void tcp_resend_data(struct k_work *work)
}
} else if (ret == -ENODATA) {
conn->data_mode = TCP_DATA_MODE_SEND;
if (!tcp_window_full(conn)) {
k_sem_give(&conn->tx_sem);
}
goto out;
}
@ -1253,6 +1264,7 @@ static struct tcp *tcp_conn_alloc(struct net_context *context)
k_mutex_init(&conn->lock);
k_fifo_init(&conn->recv_data);
k_sem_init(&conn->connect_sem, 0, K_SEM_MAX_LIMIT);
k_sem_init(&conn->tx_sem, 1, 1);
conn->in_connect = false;
conn->state = TCP_LISTEN;
@ -1822,6 +1834,12 @@ static void tcp_in(struct tcp *conn, struct net_pkt *pkt)
conn->send_win = max_win;
}
if (tcp_window_full(conn)) {
(void)k_sem_take(&conn->tx_sem, K_NO_WAIT);
} else {
k_sem_give(&conn->tx_sem);
}
}
next_state:
@ -1968,6 +1986,11 @@ next_state:
} else {
conn->unacked_len -= len_acked;
}
if (!tcp_window_full(conn)) {
k_sem_give(&conn->tx_sem);
}
conn_seq(conn, + len_acked);
net_stats_update_tcp_seg_recv(conn->iface);
@ -2908,6 +2931,13 @@ const char *net_tcp_state_str(enum tcp_state state)
return tcp_state_to_str(state, false);
}
struct k_sem *net_tcp_tx_sem_get(struct net_context *context)
{
struct tcp *conn = context->tcp;
return &conn->tx_sem;
}
void net_tcp_init(void)
{
#if defined(CONFIG_NET_TEST_PROTOCOL)

View file

@ -367,6 +367,16 @@ void net_tcp_init(void);
#define net_tcp_init(...)
#endif
/**
* @brief Obtain a semaphore indicating if transfers are blocked (either due to
* filling TX window or entering retransmission mode).
*
* @param context Network context
*
* @return semaphore indicating if transfers are blocked
*/
struct k_sem *net_tcp_tx_sem_get(struct net_context *context);
#ifdef __cplusplus
}
#endif

View file

@ -234,6 +234,7 @@ struct tcp { /* TCP connection */
};
struct k_mutex lock;
struct k_sem connect_sem; /* semaphore for blocking connect */
struct k_sem tx_sem; /* Semaphore indicating if transfers are blocked . */
struct k_fifo recv_data; /* temp queue before passing data to app */
struct tcp_options recv_options;
struct tcp_options send_options;