net: tcp: Implement a fast retransmit algorithm
Instead of waiting for the retransmit timeout, retransmit as soon as missing data is deduced based on a triple-duplicate ACK. Increase the number of buffers in the testcase, to allow for at least 4 packets in flight to trigger the triple-duplicate ACK. Signed-off-by: Sjors Hettinga <s.a.hettinga@gmail.com>
This commit is contained in:
parent
9d772efbbc
commit
f2d94a7f5c
|
@ -424,6 +424,18 @@ config NET_TCP_RANDOMIZED_RTO
|
|||
a second collision is reduced and it reduces furter the more
|
||||
retransmissions occur.
|
||||
|
||||
config NET_TCP_FAST_RETRANSMIT
|
||||
bool "Fast-retry algorithm based on the number of duplicated ACKs"
|
||||
depends on NET_TCP
|
||||
default y
|
||||
help
|
||||
When a packet is lost, the receiver will keep acknowledging the
|
||||
sequence number of the last correctly received byte. Upon reception
|
||||
of a sequence of acknowledgements for the same sequence number,
|
||||
this can be deduced as that the packet afterwards must have been lost.
|
||||
In that case a retransmission is triggerd to avoid having to wait for
|
||||
the retransmit timer to elapse.
|
||||
|
||||
config NET_TCP_MAX_SEND_WINDOW_SIZE
|
||||
int "Maximum sending window size to use"
|
||||
depends on NET_TCP
|
||||
|
|
|
@ -31,6 +31,7 @@ LOG_MODULE_REGISTER(net_tcp, CONFIG_NET_TCP_LOG_LEVEL);
|
|||
#define FIN_TIMEOUT K_MSEC(tcp_fin_timeout_ms)
|
||||
#define ACK_DELAY K_MSEC(100)
|
||||
#define ZWP_MAX_DELAY_MS 120000
|
||||
#define DUPLICATE_ACK_RETRANSMIT_TRHESHOLD 3
|
||||
|
||||
static int tcp_rto = CONFIG_NET_TCP_INIT_RETRANSMISSION_TIMEOUT;
|
||||
static int tcp_retries = CONFIG_NET_TCP_RETRY_COUNT;
|
||||
|
@ -1424,6 +1425,9 @@ static struct tcp *tcp_conn_alloc(struct net_context *context)
|
|||
conn->state = TCP_LISTEN;
|
||||
conn->recv_win_max = tcp_window;
|
||||
conn->tcp_nodelay = false;
|
||||
#ifdef CONFIG_NET_TCP_FAST_RETRANSMIT
|
||||
conn->dup_ack_cnt = 0;
|
||||
#endif
|
||||
|
||||
/* Set the recv_win with the rcvbuf configured for the socket. */
|
||||
if (IS_ENABLED(CONFIG_NET_CONTEXT_RCVBUF) &&
|
||||
|
@ -2154,7 +2158,43 @@ next_state:
|
|||
break;
|
||||
}
|
||||
|
||||
if (th && net_tcp_seq_cmp(th_ack(th), conn->seq) > 0) {
|
||||
#ifdef CONFIG_NET_TCP_FAST_RETRANSMIT
|
||||
if (th && (net_tcp_seq_cmp(th_ack(th), conn->seq) == 0)) {
|
||||
/* Only if there is pending data, increment the duplicate ack count */
|
||||
if (conn->send_data_total > 0) {
|
||||
/* There could be also payload, only without payload account them */
|
||||
if (len == 0) {
|
||||
/* Increment the duplicate acc counter,
|
||||
* but maximize the value
|
||||
*/
|
||||
conn->dup_ack_cnt = MIN(conn->dup_ack_cnt + 1,
|
||||
DUPLICATE_ACK_RETRANSMIT_TRHESHOLD + 1);
|
||||
}
|
||||
} else {
|
||||
conn->dup_ack_cnt = 0;
|
||||
}
|
||||
|
||||
/* Only do fast retransmit when not already in a resend state */
|
||||
if ((conn->data_mode == TCP_DATA_MODE_SEND) &&
|
||||
(conn->dup_ack_cnt == DUPLICATE_ACK_RETRANSMIT_TRHESHOLD)) {
|
||||
/* Apply a fast retransmit */
|
||||
int temp_unacked_len = conn->unacked_len;
|
||||
|
||||
conn->unacked_len = 0;
|
||||
|
||||
ret = tcp_send_data(conn);
|
||||
if (ret < 0) {
|
||||
/* Retry at the next duplicate ack */
|
||||
conn->dup_ack_cnt--;
|
||||
}
|
||||
|
||||
/* Restore the current transmission */
|
||||
conn->unacked_len = temp_unacked_len;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (th && (net_tcp_seq_cmp(th_ack(th), conn->seq) > 0)) {
|
||||
uint32_t len_acked = th_ack(th) - conn->seq;
|
||||
|
||||
NET_DBG("conn: %p len_acked=%u", conn, len_acked);
|
||||
|
@ -2172,6 +2212,11 @@ next_state:
|
|||
break;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_TCP_FAST_RETRANSMIT
|
||||
/* New segment, reset duplicate ack counter */
|
||||
conn->dup_ack_cnt = 0;
|
||||
#endif
|
||||
|
||||
conn->send_data_total -= len_acked;
|
||||
if (conn->unacked_len < len_acked) {
|
||||
conn->unacked_len = 0;
|
||||
|
@ -2229,9 +2274,14 @@ next_state:
|
|||
if (th_seq(th) == conn->ack) {
|
||||
verdict = tcp_data_received(conn, pkt, &len);
|
||||
} else if (net_tcp_seq_greater(conn->ack, th_seq(th))) {
|
||||
if ((len > 0) || FL(&fl, &, SYN)) {
|
||||
tcp_out(conn, ACK); /* peer has resent */
|
||||
}
|
||||
/* This should handle the acknowledgements of keep alive
|
||||
* packets and retransmitted data.
|
||||
* RISK:
|
||||
* There is a tiny risk of creating a ACK loop this way when
|
||||
* both ends of the connection are out of order due to packet
|
||||
* loss is a simulatanious bidirectional data flow.
|
||||
*/
|
||||
tcp_out(conn, ACK); /* peer has resent */
|
||||
|
||||
net_stats_update_tcp_seg_ackerr(conn->iface);
|
||||
} else if (CONFIG_NET_TCP_RECV_QUEUE_TIMEOUT) {
|
||||
|
|
|
@ -273,6 +273,9 @@ struct tcp { /* TCP connection */
|
|||
uint16_t rto;
|
||||
#endif
|
||||
uint8_t send_data_retries;
|
||||
#ifdef CONFIG_NET_TCP_FAST_RETRANSMIT
|
||||
uint8_t dup_ack_cnt;
|
||||
#endif
|
||||
uint8_t zwp_retries;
|
||||
bool in_retransmission : 1;
|
||||
bool in_connect : 1;
|
||||
|
|
|
@ -35,8 +35,10 @@ CONFIG_NET_STATISTICS=y
|
|||
CONFIG_NET_STATISTICS_IPV4=y
|
||||
CONFIG_NET_STATISTICS_USER_API=y
|
||||
|
||||
CONFIG_NET_PKT_RX_COUNT=8
|
||||
CONFIG_NET_PKT_TX_COUNT=8
|
||||
CONFIG_NET_PKT_RX_COUNT=16
|
||||
CONFIG_NET_PKT_TX_COUNT=16
|
||||
CONFIG_NET_BUF_RX_COUNT=64
|
||||
CONFIG_NET_BUF_TX_COUNT=64
|
||||
|
||||
# Reduce the retry count, so the close always finishes within a second
|
||||
CONFIG_NET_TCP_RETRY_COUNT=3
|
||||
|
|
Loading…
Reference in a new issue