net: tcp: Allow to explicitly manage TCP receive window
This fixes the existing situation that "if application buffers data, it's the problem of application". It's actually the problem of the stack, as it doesn't allow application to control receive window, and without this control, any buffer will overflow, peer packets will be dropped, peer won't receive acks for them, and will employ exponential backoff, the connection will crawl to a halt. This patch adds net_context_tcp_recved() function which an application must explicitly call when it *processes* data, to advance receive window. Jira: ZEP-1999 Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
This commit is contained in:
parent
09bcd8ee74
commit
19ff963693
4 changed files with 73 additions and 13 deletions
|
@ -740,6 +740,29 @@ int net_context_recv(struct net_context *context,
|
|||
s32_t timeout,
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Update TCP receive window for context.
|
||||
*
|
||||
* @details This function should be used by an application which
|
||||
* doesn't fully process incoming data in its receive callback,
|
||||
* but for example, queues it. In this case, receive callback
|
||||
* should decrease the window (call this function with a negative
|
||||
* value) by the size of queued data, and function(s) which dequeue
|
||||
* data - with positive value corresponding to the dequeued size.
|
||||
* For example, if receive callback gets a packet with the data
|
||||
* size of 256 and queues it, it should call this function with
|
||||
* delta of -256. If a function extracts 10 bytes of the queued
|
||||
* data, it should call it with delta of 10.
|
||||
*
|
||||
* @param context The TCP network context to use.
|
||||
* @param delta Size, in bytes, by which to increase TCP receive
|
||||
* window (negative value to decrease).
|
||||
*
|
||||
* @return 0 if ok, < 0 if error
|
||||
*/
|
||||
int net_context_update_recv_wnd(struct net_context *context,
|
||||
s32_t delta);
|
||||
|
||||
/**
|
||||
* @typedef net_context_cb_t
|
||||
* @brief Callback used while iterating over network contexts
|
||||
|
|
|
@ -1118,6 +1118,7 @@ NET_CONN_CB(tcp_established)
|
|||
struct net_tcp_hdr hdr, *tcp_hdr;
|
||||
enum net_verdict ret;
|
||||
u8_t tcp_flags;
|
||||
u16_t data_len;
|
||||
|
||||
NET_ASSERT(context && context->tcp);
|
||||
|
||||
|
@ -1187,10 +1188,18 @@ NET_CONN_CB(tcp_established)
|
|||
}
|
||||
|
||||
set_appdata_values(pkt, IPPROTO_TCP);
|
||||
context->tcp->send_ack += net_pkt_appdatalen(pkt);
|
||||
|
||||
data_len = net_pkt_appdatalen(pkt);
|
||||
if (data_len > net_tcp_get_recv_wnd(context->tcp)) {
|
||||
NET_ERR("Context %p: overflow of recv window (%d vs %d), pkt dropped",
|
||||
context, net_tcp_get_recv_wnd(context->tcp), data_len);
|
||||
return NET_DROP;
|
||||
}
|
||||
|
||||
ret = packet_received(conn, pkt, context->tcp->recv_user_data);
|
||||
|
||||
context->tcp->send_ack += data_len;
|
||||
|
||||
if (tcp_flags & NET_TCP_FIN) {
|
||||
/* Sending an ACK in the CLOSE_WAIT state will transition to
|
||||
* LAST_ACK state
|
||||
|
@ -2426,6 +2435,29 @@ int net_context_recv(struct net_context *context,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int net_context_update_recv_wnd(struct net_context *context,
|
||||
s32_t delta)
|
||||
{
|
||||
#if defined(CONFIG_NET_TCP)
|
||||
s32_t new_win;
|
||||
|
||||
if (!context->tcp) {
|
||||
NET_ERR("context->tcp == NULL");
|
||||
return -EPROTOTYPE;
|
||||
}
|
||||
|
||||
new_win = context->tcp->recv_wnd + delta;
|
||||
if (new_win < 0 || new_win > UINT16_MAX) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
context->tcp->recv_wnd = new_win;
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return -EPROTOTYPE;
|
||||
#endif
|
||||
}
|
||||
void net_context_foreach(net_context_cb_t cb, void *user_data)
|
||||
{
|
||||
int i;
|
||||
|
|
|
@ -220,6 +220,7 @@ struct net_tcp *net_tcp_alloc(struct net_context *context)
|
|||
|
||||
tcp_context[i].send_seq = tcp_init_isn();
|
||||
tcp_context[i].recv_max_ack = tcp_context[i].send_seq + 1u;
|
||||
tcp_context[i].recv_wnd = min(NET_TCP_MAX_WIN, NET_TCP_BUF_MAX_LEN);
|
||||
|
||||
tcp_context[i].accept_cb = NULL;
|
||||
|
||||
|
@ -406,17 +407,9 @@ static struct net_pkt *prepare_segment(struct net_tcp *tcp,
|
|||
return pkt;
|
||||
}
|
||||
|
||||
static inline u32_t get_recv_wnd(struct net_tcp *tcp)
|
||||
u32_t net_tcp_get_recv_wnd(const struct net_tcp *tcp)
|
||||
{
|
||||
ARG_UNUSED(tcp);
|
||||
|
||||
/* We don't queue received data inside the stack, we hand off
|
||||
* packets to synchronous callbacks (who can queue if they
|
||||
* want, but it's not our business). So the available window
|
||||
* size is always the same. There are two configurables to
|
||||
* check though.
|
||||
*/
|
||||
return min(NET_TCP_MAX_WIN, NET_TCP_BUF_MAX_LEN);
|
||||
return tcp->recv_wnd;
|
||||
}
|
||||
|
||||
int net_tcp_prepare_segment(struct net_tcp *tcp, u8_t flags,
|
||||
|
@ -482,7 +475,7 @@ int net_tcp_prepare_segment(struct net_tcp *tcp, u8_t flags,
|
|||
seq++;
|
||||
}
|
||||
|
||||
wnd = get_recv_wnd(tcp);
|
||||
wnd = net_tcp_get_recv_wnd(tcp);
|
||||
|
||||
segment.src_addr = (struct sockaddr_ptr *)local;
|
||||
segment.dst_addr = remote;
|
||||
|
@ -1054,7 +1047,8 @@ bool net_tcp_validate_seq(struct net_tcp *tcp, struct net_pkt *pkt)
|
|||
return (net_tcp_seq_cmp(sys_get_be32(tcp_hdr->seq),
|
||||
tcp->send_ack) >= 0) &&
|
||||
(net_tcp_seq_cmp(sys_get_be32(tcp_hdr->seq),
|
||||
tcp->send_ack + get_recv_wnd(tcp)) < 0);
|
||||
tcp->send_ack
|
||||
+ net_tcp_get_recv_wnd(tcp)) < 0);
|
||||
}
|
||||
|
||||
struct net_tcp_hdr *net_tcp_get_hdr(struct net_pkt *pkt,
|
||||
|
|
|
@ -154,6 +154,8 @@ struct net_tcp {
|
|||
* Semaphore to signal TCP connection completion
|
||||
*/
|
||||
struct k_sem connect_wait;
|
||||
|
||||
u16_t recv_wnd;
|
||||
};
|
||||
|
||||
static inline bool net_tcp_is_used(struct net_tcp *tcp)
|
||||
|
@ -342,6 +344,15 @@ void net_tcp_ack_received(struct net_context *ctx, u32_t ack);
|
|||
*/
|
||||
u16_t net_tcp_get_recv_mss(const struct net_tcp *tcp);
|
||||
|
||||
/**
|
||||
* @brief Returns the receive window for a given TCP context
|
||||
*
|
||||
* @param tcp TCP context
|
||||
*
|
||||
* @return Current TCP receive window
|
||||
*/
|
||||
u32_t net_tcp_get_recv_wnd(const struct net_tcp *tcp);
|
||||
|
||||
/**
|
||||
* @brief Obtains the state for a TCP context
|
||||
*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue