Skip to content

Commit

Permalink
net: tcp: Implement TCP new Reno collision avoidance
Browse files Browse the repository at this point in the history
To avoid a TCP connection from collapsing a link, implement a collision
avoidance algorithm. Initially TCP new Reno is implemented for its
simplicity.

Signed-off-by: Sjors Hettinga <s.a.hettinga@gmail.com>
  • Loading branch information
ssharks committed Jul 6, 2023
1 parent 2096006 commit 647302c
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 0 deletions.
8 changes: 8 additions & 0 deletions subsys/net/ip/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,14 @@ config NET_TCP_FAST_RETRANSMIT
In that case a retransmission is triggerd to avoid having to wait for
the retransmit timer to elapse.

config NET_TCP_CONGESTION_AVOIDANCE
bool "Implement a collision avoidance algorithm in TCP"
depends on NET_TCP
default y
help
To avoid overstressing a link reduce the transmission rate as soon as
packets are starting to drop.

config NET_TCP_MAX_SEND_WINDOW_SIZE
int "Maximum sending window size to use"
depends on NET_TCP
Expand Down
68 changes: 68 additions & 0 deletions subsys/net/ip/tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,44 @@ static void tcp_derive_rto(struct tcp *conn)
#endif
}

#ifdef CONFIG_NET_TCP_CONGESTION_AVOIDANCE
static void tcp_new_reno_init(struct tcp *conn)
{
conn->ca.congestion_win = conn_mss(conn) * TCP_CONGESTION_INITIAL_WIN;
conn->ca.ssthresh = conn_mss(conn) * TCP_CONGESTION_INITIAL_SSTHRESH;
}

static void tcp_new_reno_fast_retransmit(struct tcp *conn)
{
conn->ca.congestion_win = conn->ca.congestion_win / 2;
conn->ca.ssthresh = conn->ca.congestion_win;
}

static void tcp_new_reno_timeout(struct tcp *conn)
{
conn->ca.congestion_win = conn_mss(conn) * TCP_CONGESTION_INITIAL_WIN;
conn->ca.ssthresh = conn->ca.ssthresh / 2;
}

static void tcp_new_reno_pkts_acked(struct tcp *conn)
{
int32_t new_win = conn->ca.congestion_win;

if (conn->ca.state == TCP_NEW_RENO_RAMPUP) {
new_win += conn_mss(conn);
conn->ca.congestion_win += conn_mss(conn);
} else {
int32_t mss = conn_mss(conn);

new_win += (mss * mss) / conn->ca.congestion_win;
}
conn->ca.congestion_win = MIN(new_win, UINT16_MAX);
if (conn->ca.congestion_win > conn->ca.ssthresh) {
conn->ca.state = TCP_NEW_RENO_LINEAR;
}
}
#endif

static void tcp_send_queue_flush(struct tcp *conn)
{
struct net_pkt *pkt;
Expand Down Expand Up @@ -1140,6 +1178,9 @@ static int tcp_pkt_peek(struct net_pkt *to, struct net_pkt *from, size_t pos,
static bool tcp_window_full(struct tcp *conn)
{
bool window_full = (conn->send_data_total >= conn->send_win);
#ifdef CONFIG_NET_TCP_CONGESTION_AVOIDANCE
window_full |= (conn->send_data_total >= conn->ca.congestion_win);
#endif

NET_DBG("conn: %p window_full=%hu", conn, window_full);

Expand All @@ -1162,6 +1203,13 @@ static int tcp_unsent_len(struct tcp *conn)
unsent_len = 0;
} else {
unsent_len = MIN(unsent_len, conn->send_win - conn->unacked_len);
#if CONFIG_NET_TCP_CONGESTION_AVOIDANCE
if (conn->unacked_len >= conn->ca.congestion_win) {
unsent_len = 0;
} else {
unsent_len = MIN(unsent_len, conn->ca.congestion_win - conn->unacked_len);
}
#endif
}
out:
NET_DBG("unsent_len=%d", unsent_len);
Expand Down Expand Up @@ -1306,6 +1354,10 @@ static void tcp_resend_data(struct k_work *work)
goto out;
}

#ifdef CONFIG_NET_TCP_CONGESTION_AVOIDANCE
tcp_new_reno_timeout(conn);
#endif

conn->data_mode = TCP_DATA_MODE_RESEND;
conn->unacked_len = 0;

Expand Down Expand Up @@ -1486,6 +1538,11 @@ static struct tcp *tcp_conn_alloc(void)
conn->dup_ack_cnt = 0;
#endif

#ifdef CONFIG_NET_TCP_CONGESTION_AVOIDANCE
tcp_new_reno_init(conn);
#endif


/* The ISN value will be set when we get the connection attempt or
* when trying to create a connection.
*/
Expand Down Expand Up @@ -2211,6 +2268,10 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
net_context_set_state(conn->context,
NET_CONTEXT_CONNECTED);

#ifdef CONFIG_NET_TCP_CONGESTION_AVOIDANCE
tcp_new_reno_init(conn);
#endif

if (conn->accepted_conn) {
if (conn->accepted_conn->accept_cb) {
conn->accepted_conn->accept_cb(
Expand Down Expand Up @@ -2340,6 +2401,10 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)

/* Restore the current transmission */
conn->unacked_len = temp_unacked_len;

#ifdef CONFIG_NET_TCP_CONGESTION_AVOIDANCE
tcp_new_reno_fast_retransmit(conn);
#endif
}
}
#endif
Expand All @@ -2366,6 +2431,9 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
/* New segment, reset duplicate ack counter */
conn->dup_ack_cnt = 0;
#endif
#ifdef CONFIG_NET_TCP_CONGESTION_AVOIDANCE
tcp_new_reno_pkts_acked(conn);
#endif

conn->send_data_total -= len_acked;
if (conn->unacked_len < len_acked) {
Expand Down
24 changes: 24 additions & 0 deletions subsys/net/ip/tcp_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@

#include "tp.h"

#ifdef CONFIG_NET_TCP_CONGESTION_AVOIDANCE
/* Define the number of MSS sections the congestion window is initialized at */
#define TCP_CONGESTION_INITIAL_WIN 1
#define TCP_CONGESTION_INITIAL_SSTHRESH 3

#endif

#define is(_a, _b) (strcmp((_a), (_b)) == 0)

#ifndef MIN3
Expand Down Expand Up @@ -223,6 +230,20 @@ struct tcp_options {
bool wnd_found : 1;
};

#ifdef CONFIG_NET_TCP_CONGESTION_AVOIDANCE

enum tcp_new_reno_state {
TCP_NEW_RENO_RAMPUP = 1,
TCP_NEW_RENO_LINEAR
};

struct tcp_collision_avoidance_reno {
uint16_t congestion_win;
uint16_t ssthresh;
enum tcp_new_reno_state state;
};
#endif

struct tcp { /* TCP connection */
sys_snode_t next;
struct net_context *context;
Expand Down Expand Up @@ -273,6 +294,9 @@ struct tcp { /* TCP connection */
uint16_t send_win;
#ifdef CONFIG_NET_TCP_RANDOMIZED_RTO
uint16_t rto;
#endif
#ifdef CONFIG_NET_TCP_CONGESTION_AVOIDANCE
struct tcp_collision_avoidance_reno ca;
#endif
uint8_t send_data_retries;
#ifdef CONFIG_NET_TCP_FAST_RETRANSMIT
Expand Down

0 comments on commit 647302c

Please sign in to comment.