From 303e6499bd6a7fd661126fc2a61890eca2f3cb79 Mon Sep 17 00:00:00 2001 From: Pelle Windestam Date: Fri, 20 Apr 2018 07:37:51 -0600 Subject: [PATCH] net/tcp: Extended support for sending to non-blocking tcp sockets. --- net/tcp/tcp.h | 19 +++++++++++ net/tcp/tcp_send_buffered.c | 42 ++++++++++++++++++++++--- net/tcp/tcp_wrbuffer.c | 63 ++++++++++++++++++++++++++++++++++++- 3 files changed, 119 insertions(+), 5 deletions(-) diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h index d800990faf..6a4796f07e 100644 --- a/net/tcp/tcp.h +++ b/net/tcp/tcp.h @@ -1416,6 +1416,25 @@ void tcp_wrbuffer_initialize(void); struct tcp_wrbuffer_s; FAR struct tcp_wrbuffer_s *tcp_wrbuffer_alloc(void); + +/**************************************************************************** + * Name: tcp_wrbuffer_tryalloc + * + * Description: + * Try to allocate a TCP write buffer by taking a pre-allocated buffer from + * the free list. This function is called from TCP logic when a buffer + * of TCP data is about to be sent if the socket is non-blocking. Returns + * immediately if allocation fails. + * + * Input parameters: + * None + * + * Assumptions: + * Called from user logic with the network locked. + * + ****************************************************************************/ + +FAR struct tcp_wrbuffer_s *tcp_wrbuffer_tryalloc(void); #endif /* CONFIG_NET_TCP_WRITE_BUFFERS */ /**************************************************************************** diff --git a/net/tcp/tcp_send_buffered.c b/net/tcp/tcp_send_buffered.c index 96870bd68e..c808117ee2 100644 --- a/net/tcp/tcp_send_buffered.c +++ b/net/tcp/tcp_send_buffered.c @@ -1086,13 +1086,21 @@ ssize_t psock_tcp_send(FAR struct socket *psock, FAR const void *buf, */ net_lock(); - wrb = tcp_wrbuffer_alloc(); - if (!wrb) + if (_SS_ISNONBLOCK(psock->s_flags)) + { + wrb = tcp_wrbuffer_tryalloc(); + } + else + { + wrb = tcp_wrbuffer_alloc(); + } + + if (wrb == NULL) { /* A buffer allocation error occurred */ nerr("ERROR: Failed to allocate write buffer\n"); - ret = -ENOMEM; + ret = _SS_ISNONBLOCK(psock->s_flags) ? -EAGAIN : -ENOMEM; goto errout_with_lock; } @@ -1110,7 +1118,7 @@ ssize_t psock_tcp_send(FAR struct socket *psock, FAR const void *buf, /* A buffer allocation error occurred */ nerr("ERROR: Failed to allocate callback\n"); - ret = -ENOMEM; + ret = _SS_ISNONBLOCK(psock->s_flags) ? -EAGAIN : -ENOMEM; goto errout_with_wrb; } @@ -1132,7 +1140,33 @@ ssize_t psock_tcp_send(FAR struct socket *psock, FAR const void *buf, if (_SS_ISNONBLOCK(psock->s_flags)) { + /* The return value from TCP_WBTRYCOPYIN is either OK or + * -ENOMEM if less than the entire data chunk could be allocated. + * If -ENOMEM is returned, check if at least a part of the data + * chunk was allocated. If more than zero bytes were sent + * we return that number and let the caller deal with sending the + * remaining data. + */ + result = TCP_WBTRYCOPYIN(wrb, (FAR uint8_t *)buf, len); + if (result == -ENOMEM) + { + if (TCP_WBPKTLEN(wrb) > 0) + { + ninfo("INFO: Allocated part of the requested data\n"); + result = TCP_WBPKTLEN(wrb); + } + else + { + nerr("ERROR: Failed to add data to the I/O buffer chain\n"); + ret = -EWOULDBLOCK; + goto errout_with_wrb; + } + } + else + { + result = len; + } } else { diff --git a/net/tcp/tcp_wrbuffer.c b/net/tcp/tcp_wrbuffer.c index c203285ce7..ef0eb84f9a 100644 --- a/net/tcp/tcp_wrbuffer.c +++ b/net/tcp/tcp_wrbuffer.c @@ -1,7 +1,8 @@ /**************************************************************************** * net/tcp/tcp_wrbuffer.c * - * Copyright (C) 2007-2009, 2013-2014 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2013-2014, 2018 Gregory Nutt. All rights + * reserved. * Author: Gregory Nutt * Jason Jiang * @@ -169,6 +170,66 @@ FAR struct tcp_wrbuffer_s *tcp_wrbuffer_alloc(void) return wrb; } +/**************************************************************************** + * Name: tcp_wrbuffer_tryalloc + * + * Description: + * Try to allocate a TCP write buffer by taking a pre-allocated buffer from + * the free list. This function is called from TCP logic when a buffer + * of TCP data is about to be sent on a non-blocking socket. Returns + * immediately if the allocation failed. + * + * Input parameters: + * None + * + * Assumptions: + * Called from user logic with the network locked. Will return if no buffer + * is available. + * + ****************************************************************************/ + +FAR struct tcp_wrbuffer_s *tcp_wrbuffer_tryalloc(void) +{ + FAR struct tcp_wrbuffer_s *wrb; + + /* We need to allocate two things: (1) A write buffer structure and (2) + * at least one I/O buffer to start the chain. + * + * Allocate the write buffer structure first then the IOBG. In order to + * avoid deadlocks, we will need to free the IOB first, then the write + * buffer + */ + + if (tcp_wrbuffer_test() == OK) + { + DEBUGVERIFY(net_lockedwait(&g_wrbuffer.sem)); + } + else + { + return NULL; + } + + /* Now, we are guaranteed to have a write buffer structure reserved + * for us in the free list. + */ + + wrb = (FAR struct tcp_wrbuffer_s *)sq_remfirst(&g_wrbuffer.freebuffers); + DEBUGASSERT(wrb); + memset(wrb, 0, sizeof(struct tcp_wrbuffer_s)); + + /* Now get the first I/O buffer for the write buffer structure */ + + wrb->wb_iob = iob_tryalloc(false); + if (!wrb->wb_iob) + { + nerr("ERROR: Failed to allocate I/O buffer\n"); + tcp_wrbuffer_release(wrb); + return NULL; + } + + return wrb; +} + /**************************************************************************** * Name: tcp_wrbuffer_release *