net: tcp: Implement Nagle's algorithm
To improve the performance with small chunks send, implement Nagle's algorithm. Provide the option TCP_NODELAY to disable the algorithm. Signed-off-by: Sjors Hettinga <s.a.hettinga@gmail.com>
This commit is contained in:
parent
c44ef044b8
commit
e097d95c66
4 changed files with 160 additions and 4 deletions
|
@ -848,6 +848,37 @@ static int ip_header_add(struct tcp *conn, struct net_pkt *pkt)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int set_tcp_nodelay(struct tcp *conn, const void *value, size_t len)
|
||||
{
|
||||
int no_delay_int;
|
||||
|
||||
if (len != sizeof(int)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
no_delay_int = *(int *)value;
|
||||
|
||||
if ((no_delay_int < 0) || (no_delay_int > 1)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
conn->tcp_nodelay = (bool)no_delay_int;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_tcp_nodelay(struct tcp *conn, void *value, size_t *len)
|
||||
{
|
||||
int no_delay_int = (int)conn->tcp_nodelay;
|
||||
|
||||
*((int *)value) = no_delay_int;
|
||||
|
||||
if (len) {
|
||||
*len = sizeof(int);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int net_tcp_set_mss_opt(struct tcp *conn, struct net_pkt *pkt)
|
||||
{
|
||||
NET_PKT_DATA_ACCESS_DEFINE(mss_opt_access, struct tcp_mss_option);
|
||||
|
@ -1097,6 +1128,19 @@ static int tcp_send_queued_data(struct tcp *conn)
|
|||
}
|
||||
|
||||
while (tcp_unsent_len(conn) > 0) {
|
||||
/* Implement Nagle's algorithm */
|
||||
if ((conn->tcp_nodelay == false) && (conn->unacked_len > 0)) {
|
||||
/* If there is already pending data */
|
||||
if (tcp_unsent_len(conn) < conn_mss(conn)) {
|
||||
/* The number of bytes to be transmitted is less than an MSS,
|
||||
* skip transmission for now.
|
||||
* Wait for more data to be transmitted or all pending data
|
||||
* being acknowledged.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = tcp_send_data(conn);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
|
@ -1301,6 +1345,7 @@ static struct tcp *tcp_conn_alloc(struct net_context *context)
|
|||
conn->in_connect = false;
|
||||
conn->state = TCP_LISTEN;
|
||||
conn->recv_win = tcp_window;
|
||||
conn->tcp_nodelay = false;
|
||||
|
||||
/* Set the recv_win with the rcvbuf configured for the socket. */
|
||||
if (IS_ENABLED(CONFIG_NET_CONTEXT_RCVBUF) &&
|
||||
|
@ -2994,6 +3039,56 @@ uint16_t net_tcp_get_supported_mss(const struct tcp *conn)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int net_tcp_set_option(struct net_context *context,
|
||||
enum tcp_conn_option option,
|
||||
const void *value, size_t len)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
NET_ASSERT(context);
|
||||
|
||||
struct tcp *conn = context->tcp;
|
||||
|
||||
NET_ASSERT(conn);
|
||||
|
||||
k_mutex_lock(&conn->lock, K_FOREVER);
|
||||
|
||||
switch (option) {
|
||||
case TCP_OPT_NODELAY:
|
||||
ret = set_tcp_nodelay(conn, value, len);
|
||||
break;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&conn->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int net_tcp_get_option(struct net_context *context,
|
||||
enum tcp_conn_option option,
|
||||
void *value, size_t *len)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
NET_ASSERT(context);
|
||||
|
||||
struct tcp *conn = context->tcp;
|
||||
|
||||
NET_ASSERT(conn);
|
||||
|
||||
k_mutex_lock(&conn->lock, K_FOREVER);
|
||||
|
||||
switch (option) {
|
||||
case TCP_OPT_NODELAY:
|
||||
ret = get_tcp_nodelay(conn, value, len);
|
||||
break;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&conn->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *net_tcp_state_str(enum tcp_state state)
|
||||
{
|
||||
return tcp_state_to_str(state, false);
|
||||
|
|
|
@ -30,6 +30,10 @@ extern "C" {
|
|||
|
||||
#include "tcp_private.h"
|
||||
|
||||
enum tcp_conn_option {
|
||||
TCP_OPT_NODELAY = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Calculates and returns the MSS for a given TCP context
|
||||
*
|
||||
|
@ -367,6 +371,57 @@ void net_tcp_init(void);
|
|||
#define net_tcp_init(...)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Set tcp specific options of a socket
|
||||
*
|
||||
* @param context Network context
|
||||
*
|
||||
* @return 0 on success, -EINVAL if the value is not allowed
|
||||
*/
|
||||
#if defined(CONFIG_NET_NATIVE_TCP)
|
||||
int net_tcp_set_option(struct net_context *context,
|
||||
enum tcp_conn_option option,
|
||||
const void *value, size_t len);
|
||||
#else
|
||||
static inline int net_tcp_set_option(struct net_context *context,
|
||||
enum tcp_conn_option option,
|
||||
const void *value, size_t len)
|
||||
{
|
||||
ARG_UNUSED(context);
|
||||
ARG_UNUSED(option);
|
||||
ARG_UNUSED(value);
|
||||
ARG_UNUSED(len);
|
||||
|
||||
return -EPROTONOSUPPORT;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Obtain tcp specific options of a socket
|
||||
*
|
||||
* @param context Network context
|
||||
*
|
||||
* @return 0 on success
|
||||
*/
|
||||
#if defined(CONFIG_NET_NATIVE_TCP)
|
||||
int net_tcp_get_option(struct net_context *context,
|
||||
enum tcp_conn_option option,
|
||||
void *value, size_t *len);
|
||||
#else
|
||||
static inline int net_tcp_get_option(struct net_context *context,
|
||||
enum tcp_conn_option option,
|
||||
void *value, size_t *len)
|
||||
{
|
||||
ARG_UNUSED(context);
|
||||
ARG_UNUSED(option);
|
||||
ARG_UNUSED(value);
|
||||
ARG_UNUSED(len);
|
||||
|
||||
return -EPROTONOSUPPORT;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Obtain a semaphore indicating if transfers are blocked (either due to
|
||||
* filling TX window or entering retransmission mode).
|
||||
|
|
|
@ -269,6 +269,7 @@ struct tcp { /* TCP connection */
|
|||
bool in_retransmission : 1;
|
||||
bool in_connect : 1;
|
||||
bool in_close : 1;
|
||||
bool tcp_nodelay : 1;
|
||||
};
|
||||
|
||||
#define _flags(_fl, _op, _mask, _cond) \
|
||||
|
|
|
@ -1852,6 +1852,12 @@ int zsock_getsockopt_ctx(struct net_context *ctx, int level, int optname,
|
|||
}
|
||||
break;
|
||||
}
|
||||
case IPPROTO_TCP:
|
||||
switch (optname) {
|
||||
case TCP_NODELAY:
|
||||
ret = net_tcp_get_option(ctx, TCP_OPT_NODELAY, optval, optlen);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
errno = ENOPROTOOPT;
|
||||
|
@ -2099,10 +2105,9 @@ int zsock_setsockopt_ctx(struct net_context *ctx, int level, int optname,
|
|||
case IPPROTO_TCP:
|
||||
switch (optname) {
|
||||
case TCP_NODELAY:
|
||||
/* Ignore for now. Provided to let port
|
||||
* existing apps.
|
||||
*/
|
||||
return 0;
|
||||
ret = net_tcp_set_option(ctx,
|
||||
TCP_OPT_NODELAY, optval, optlen);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue