This commit implements a proper version of SO_LINGER. Not sufficiently tested on initial commit.
Squashed commit of the following: net/: Fix some naming inconsistencies, Fix final compilation issies. net/inet/inet_close(): Now that we have logic to drain the buffered TX data, we can implement a proper lingering close. net/inet,tcp,udp: Add functions to wait for write buffers to drain. net/udp: Add support for notification when the UDP write buffer becomes empty. net/tcp: Add support for notification when the TCP write buffer becomes empty.
This commit is contained in:
parent
ded1900927
commit
de5a6163d5
25
TODO
25
TODO
@ -19,7 +19,7 @@ nuttx/:
|
||||
(9) Kernel/Protected Build
|
||||
(3) C++ Support
|
||||
(5) Binary loaders (binfmt/)
|
||||
(18) Network (net/, drivers/net)
|
||||
(17) Network (net/, drivers/net)
|
||||
(4) USB (drivers/usbdev, drivers/usbhost)
|
||||
(2) Other drivers (drivers/)
|
||||
(9) Libraries (libs/libc/, libs/libm/)
|
||||
@ -1663,29 +1663,6 @@ o Network (net/, drivers/net)
|
||||
anything but a well-known point-to-point configuration
|
||||
impossible.
|
||||
|
||||
Title: SO_LINGER IMPLEMENTATION IS INCORRECT
|
||||
Description: Support for the SO_LINGER socket option is implemented but
|
||||
not correctly. Currently, it simply adds a timeout to the
|
||||
"normal" delay for the FIN to be sent with an additional
|
||||
timeout. That is not even close to the required behavior.
|
||||
Per OpenGroup.org:
|
||||
|
||||
SO_LINGER
|
||||
Lingers on a close() if data is present. This option
|
||||
controls the action taken when unsent messages queue
|
||||
on a socket and close() is performed. If SO_LINGER
|
||||
is set, the system shall block the calling thread
|
||||
during close() until it can transmit the data or
|
||||
until the time expires. If SO_LINGER is not specified,
|
||||
and close() is issued, the system handles the call
|
||||
in a way that allows the calling thread to continue
|
||||
as quickly as possible. This option takes a linger
|
||||
structure, as defined in the <sys/socket.h> header,
|
||||
to specify the state of the option and linger interval.
|
||||
|
||||
Status: Open
|
||||
Priority: Medium Low.
|
||||
|
||||
o USB (drivers/usbdev, drivers/usbhost)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -284,11 +284,13 @@ struct work_s
|
||||
|
||||
enum work_evtype_e
|
||||
{
|
||||
WORK_IOB_AVAIL = 1, /* Notify availability of an IOB */
|
||||
WORK_NET_DOWN, /* Notify that the network is down */
|
||||
WORK_TCP_READAHEAD, /* Notify that TCP read-ahead data is available */
|
||||
WORK_TCP_DISCONNECT, /* Notify loss of TCP connection */
|
||||
WORK_UDP_READAHEAD /* Notify that TCP read-ahead data is available */
|
||||
WORK_IOB_AVAIL = 1, /* Notify availability of an IOB */
|
||||
WORK_NET_DOWN, /* Notify that the network is down */
|
||||
WORK_TCP_READAHEAD, /* Notify that TCP read-ahead data is available */
|
||||
WORK_TCP_WRITEBUFFER, /* Notify that TCP write buffer is empty */
|
||||
WORK_TCP_DISCONNECT, /* Notify loss of TCP connection */
|
||||
WORK_UDP_READAHEAD, /* Notify that UDP read-ahead data is available */
|
||||
WORK_UDP_WRITEBUFFER /* Notify that UDP write buffer is empty */
|
||||
};
|
||||
|
||||
/* This structure describes one notification and is provided as input to
|
||||
|
@ -52,6 +52,10 @@ ifeq ($(CONFIG_NET_IPv6),y)
|
||||
SOCK_CSRCS += ipv6_setsockopt.c ipv6_getsockname.c ipv6_getpeername.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_NET_SOLINGER),y)
|
||||
SOCK_CSRCS += inet_txdrain.c
|
||||
endif
|
||||
|
||||
# Include inet build support
|
||||
|
||||
DEPPATH += --dep-path inet
|
||||
|
@ -305,6 +305,29 @@ ssize_t inet_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len,
|
||||
|
||||
int inet_close(FAR struct socket *psock);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inet_txdrain
|
||||
*
|
||||
* Description:
|
||||
* Wait for any buffered Tx data to be sent from the socket. This is part
|
||||
* of the implementation of SO_LINGER
|
||||
*
|
||||
* Parameters:
|
||||
* psock - Pointer to the socket structure instance
|
||||
* abstime - The absolute time when the timeout will occur
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) is returned on success; a negated error value is returned on
|
||||
* any failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NET_SOLINGER
|
||||
struct timespec;
|
||||
int inet_txdrain(FAR struct socket *psock,
|
||||
FAR const struct timespec *abstime);
|
||||
#endif
|
||||
|
||||
#undef EXTERN
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
|
@ -56,10 +56,6 @@
|
||||
#include <nuttx/net/tcp.h>
|
||||
#include <nuttx/net/udp.h>
|
||||
|
||||
#ifdef CONFIG_NET_SOLINGER
|
||||
# include <nuttx/clock.h>
|
||||
#endif
|
||||
|
||||
#include "netdev/netdev.h"
|
||||
#include "devif/devif.h"
|
||||
#include "tcp/tcp.h"
|
||||
@ -81,9 +77,6 @@ struct tcp_close_s
|
||||
FAR struct socket *cl_psock; /* Reference to the TCP socket */
|
||||
sem_t cl_sem; /* Signals disconnect completion */
|
||||
int cl_result; /* The result of the close */
|
||||
#ifdef CONFIG_NET_SOLINGER
|
||||
clock_t cl_start; /* Time close started (in ticks) */
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -91,51 +84,6 @@ struct tcp_close_s
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tcp_close_timeout
|
||||
*
|
||||
* Description:
|
||||
* Check for a timeout on a lingering close.
|
||||
*
|
||||
* Input Parameters:
|
||||
* pstate - close state structure
|
||||
*
|
||||
* Returned Value:
|
||||
* TRUE:timeout FALSE:no timeout
|
||||
*
|
||||
* Assumptions:
|
||||
* The network is locked
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#if defined(NET_TCP_HAVE_STACK) && defined(CONFIG_NET_SOLINGER)
|
||||
static inline int tcp_close_timeout(FAR struct tcp_close_s *pstate)
|
||||
{
|
||||
FAR struct socket *psock = 0;
|
||||
|
||||
/* Make sure that we are performing a lingering close */
|
||||
|
||||
if (pstate != NULL)
|
||||
{
|
||||
/* Yes Check for a timeout configured via setsockopts(SO_LINGER).
|
||||
* If none... we well let the send wait forever.
|
||||
*/
|
||||
|
||||
psock = pstate->cl_psock;
|
||||
if (psock && psock->s_linger != 0)
|
||||
{
|
||||
/* Check if the configured timeout has elapsed */
|
||||
|
||||
return net_timeo(pstate->cl_start, psock->s_linger);
|
||||
}
|
||||
}
|
||||
|
||||
/* No timeout */
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
#endif /* NET_TCP_HAVE_STACK && CONFIG_NET_SOLINGER */
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tcp_close_eventhandler
|
||||
*
|
||||
@ -202,19 +150,6 @@ static uint16_t tcp_close_eventhandler(FAR struct net_driver_s *dev,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_SOLINGER
|
||||
/* Check for a timeout. */
|
||||
|
||||
else if (pstate && tcp_close_timeout(pstate))
|
||||
{
|
||||
/* Yes.. Wake up the waiting thread and report the timeout */
|
||||
|
||||
nerr("ERROR: CLOSE timeout\n");
|
||||
pstate->cl_result = -ETIMEDOUT;
|
||||
goto end_wait;
|
||||
}
|
||||
#endif /* CONFIG_NET_SOLINGER */
|
||||
|
||||
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
|
||||
/* Check if all outstanding bytes have been ACKed */
|
||||
|
||||
@ -337,6 +272,46 @@ static inline int tcp_close_disconnect(FAR struct socket *psock)
|
||||
conn = (FAR struct tcp_conn_s *)psock->s_conn;
|
||||
DEBUGASSERT(conn != NULL);
|
||||
|
||||
#ifdef CONFIG_NET_SOLINGER
|
||||
/* SO_LINGER
|
||||
* Lingers on a close() if data is present. This option controls the
|
||||
* action taken when unsent messages queue on a socket and close() is
|
||||
* performed. If SO_LINGER is set, the system shall block the calling
|
||||
* thread during close() until it can transmit the data or until the
|
||||
* time expires. If SO_LINGER is not specified, and close() is issued,
|
||||
* the system handles the call in a way that allows the calling thread
|
||||
* to continue as quickly as possible. This option takes a linger
|
||||
* structure, as defined in the <sys/socket.h> header, to specify the
|
||||
* state of the option and linger interval.
|
||||
*/
|
||||
|
||||
linger = _SO_GETOPT(psock->s_options, SO_LINGER);
|
||||
if (linger)
|
||||
{
|
||||
/* Get the current time */
|
||||
|
||||
DEBUGVERIFY(clock_gettime(CLOCK_REALTIME, &abstime));
|
||||
|
||||
/* NOTE: s_linger's unit is deciseconds so we don't need to update
|
||||
* abstime.tv_nsec here.
|
||||
*/
|
||||
|
||||
abstime.tv_sec += psock->s_linger / DSEC_PER_SEC;
|
||||
|
||||
/* Wait until abstime for the buffered TX data to be sent. */
|
||||
|
||||
ret = inet_txdrain(psock, &abstime);
|
||||
if (ret < 0)
|
||||
{
|
||||
/* inet_txdrain may fail, but that won't stop us from closing the
|
||||
* socket.
|
||||
*/
|
||||
|
||||
nerr("ERROR: inet_txdrain() failed: %d\n", ret);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
|
||||
/* If we have a semi-permanent write buffer callback in place, then
|
||||
* is needs to be be nullified.
|
||||
@ -387,63 +362,13 @@ static inline int tcp_close_disconnect(FAR struct socket *psock)
|
||||
nxsem_init(&state.cl_sem, 0, 0);
|
||||
nxsem_setprotocol(&state.cl_sem, SEM_PRIO_NONE);
|
||||
|
||||
#ifdef CONFIG_NET_SOLINGER
|
||||
/* Record the time that we started the wait (in ticks) */
|
||||
|
||||
state.cl_start = clock_systimer();
|
||||
#endif
|
||||
|
||||
/* Notify the device driver of the availability of TX data */
|
||||
|
||||
tcp_close_txnotify(psock, conn);
|
||||
|
||||
/* Wait for the disconnect event */
|
||||
|
||||
#ifdef CONFIG_NET_SOLINGER
|
||||
/* A non-NULL value of the priv field means that lingering is
|
||||
* enabled.
|
||||
*
|
||||
* REVISIT: SO_LINGER is not really implemented. Per OpenGroup.org:
|
||||
*
|
||||
* SO_LINGER
|
||||
* Lingers on a close() if data is present. This option
|
||||
* controls the action taken when unsent messages queue
|
||||
* on a socket and close() is performed. If SO_LINGER
|
||||
* is set, the system shall block the calling thread
|
||||
* during close() until it can transmit the data or
|
||||
* until the time expires. If SO_LINGER is not specified,
|
||||
* and close() is issued, the system handles the call
|
||||
* in a way that allows the calling thread to continue
|
||||
* as quickly as possible. This option takes a linger
|
||||
* structure, as defined in the <sys/socket.h> header,
|
||||
* to specify the state of the option and linger interval.
|
||||
*
|
||||
* Here it merely adds a pointless timeout on top of the normal
|
||||
* close operation. It should first wait for all data in the
|
||||
* protocol-specific write buffers to drain.
|
||||
*/
|
||||
|
||||
linger = _SO_GETOPT(psock->s_options, SO_LINGER);
|
||||
if (linger)
|
||||
{
|
||||
DEBUGVERIFY(clock_gettime(CLOCK_REALTIME, &abstime));
|
||||
|
||||
/* NOTE: s_linger's unit is deciseconds,
|
||||
* so we don't need to update abstime.tv_nsec here.
|
||||
*/
|
||||
|
||||
abstime.tv_sec += psock->s_linger / DSEC_PER_SEC;
|
||||
|
||||
if (-ETIMEDOUT == net_timedwait(&state.cl_sem, &abstime))
|
||||
{
|
||||
state.cl_result = -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
(void)net_lockedwait(&state.cl_sem);
|
||||
}
|
||||
(void)net_lockedwait(&state.cl_sem);
|
||||
|
||||
/* We are now disconnected */
|
||||
|
||||
|
110
net/inet/inet_txdrain.c
Normal file
110
net/inet/inet_txdrain.c
Normal file
@ -0,0 +1,110 @@
|
||||
/****************************************************************************
|
||||
* net/inet/inet_txdrain.c
|
||||
*
|
||||
* Copyright (C) 2019 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name NuttX nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <nuttx/net/net.h>
|
||||
|
||||
#include "udp/udp.h"
|
||||
#include "tcp/tcp.h"
|
||||
#include "inet/inet.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: inet_txdrain
|
||||
*
|
||||
* Description:
|
||||
* Wait for any buffered Tx data to be sent from the socket. This is part
|
||||
* of the implementation of SO_LINGER
|
||||
*
|
||||
* Parameters:
|
||||
* psock - Pointer to the socket structure instance
|
||||
* abstime - The absolute time when the timeout will occur
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) is returned on success; a negated error value is returned on
|
||||
* any failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int inet_txdrain(FAR struct socket *psock,
|
||||
FAR const struct timespec *abstime)
|
||||
{
|
||||
int ret = OK;
|
||||
|
||||
DEBUGASSERT(psock != NULL);
|
||||
|
||||
/* Draining depends on the socket family */
|
||||
|
||||
switch (psock->s_type)
|
||||
{
|
||||
#if defined(NET_TCP_HAVE_STACK) && defined(CONFIG_NET_TCP_WRITE_BUFFERS)
|
||||
case SOCK_STREAM:
|
||||
{
|
||||
ret = tcp_txdrain(psock, abstime);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(NET_UDP_HAVE_STACK) && defined(CONFIG_NET_UDP_WRITE_BUFFERS)
|
||||
case SOCK_DGRAM:
|
||||
{
|
||||
ret = udp_txdrain(psock, abstime);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
/* Other protocols do no support write buffering */
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
@ -42,8 +42,12 @@ if NET_SOCKOPTS
|
||||
config NET_SOLINGER
|
||||
bool "SO_LINGER socket option"
|
||||
default n
|
||||
depends on NET_TCP_WRITE_BUFFERS || NET_UDP_WRITE_BUFFERS
|
||||
select TCP_NOTIFIER if NET_TCP
|
||||
select UDP_NOTIFIER if NET_UDP
|
||||
---help---
|
||||
Enable or disable support for the SO_LINGER socket option.
|
||||
Enable or disable support for the SO_LINGER socket option. Requires
|
||||
write buffer support.
|
||||
|
||||
endif # NET_SOCKOPTS
|
||||
endmenu # Socket Support
|
||||
|
@ -56,6 +56,9 @@ ifeq ($(CONFIG_NET_TCP_READAHEAD),y)
|
||||
SOCK_CSRCS += tcp_netpoll.c
|
||||
ifeq ($(CONFIG_TCP_NOTIFIER),y)
|
||||
SOCK_CSRCS += tcp_notifier.c
|
||||
ifeq ($(CONFIG_NET_TCP_WRITE_BUFFERS),y)
|
||||
SOCK_CSRCS += tcp_txdrain.c
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
|
@ -1558,7 +1558,7 @@ int tcp_pollteardown(FAR struct socket *psock, FAR struct pollfd *fds);
|
||||
* Input Parameters:
|
||||
* worker - The worker function to execute on the high priority work
|
||||
* queue when data is available in the TCP read-ahead buffer.
|
||||
* conn - The TCP connection where read-ahead data is needed.
|
||||
* conn - The TCP connection where read-ahead data is needed.
|
||||
* arg - A user-defined argument that will be available to the worker
|
||||
* function when it runs.
|
||||
*
|
||||
@ -1581,7 +1581,40 @@ int tcp_readahead_notifier_setup(worker_t worker,
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tcp_readahead_disconnect_setup
|
||||
* Name: tcp_writebuffer_notifier_setup
|
||||
*
|
||||
* Description:
|
||||
* Set up to perform a callback to the worker function when an TCP write
|
||||
* buffer is emptied. The worker function will execute on the high
|
||||
* priority worker thread.
|
||||
*
|
||||
* Input Parameters:
|
||||
* worker - The worker function to execute on the high priority work
|
||||
* queue when all buffer TX data has been sent.
|
||||
* conn - The TCP connection where buffer write data is pending.
|
||||
* arg - A user-defined argument that will be available to the worker
|
||||
* function when it runs.
|
||||
*
|
||||
* Returned Value:
|
||||
* > 0 - The signal notification is in place. The returned value is a
|
||||
* key that may be used later in a call to
|
||||
* tcp_notifier_teardown().
|
||||
* == 0 - There is already buffered read-ahead data. No signal
|
||||
* notification will be provided.
|
||||
* < 0 - An unexpected error occurred and no signal will be sent. The
|
||||
* returned value is a negated errno value that indicates the
|
||||
* nature of the failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_TCP_NOTIFIER
|
||||
int tcp_writebuffer_notifier_setup(worker_t worker,
|
||||
FAR struct tcp_conn_s *conn,
|
||||
FAR void *arg);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tcp_disconnect_notifier_setup
|
||||
*
|
||||
* Description:
|
||||
* Set up to perform a callback to the worker function if the TCP
|
||||
@ -1607,9 +1640,9 @@ int tcp_readahead_notifier_setup(worker_t worker,
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_TCP_NOTIFIER
|
||||
int tcp_readahead_disconnect_setup(worker_t worker,
|
||||
FAR struct tcp_conn_s *conn,
|
||||
FAR void *arg);
|
||||
int tcp_disconnect_notifier_setup(worker_t worker,
|
||||
FAR struct tcp_conn_s *conn,
|
||||
FAR void *arg);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
@ -1656,10 +1689,35 @@ int tcp_notifier_teardown(int key);
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_TCP_NOTIFIER
|
||||
#if defined(CONFIG_NET_TCP_READAHEAD) && defined(CONFIG_TCP_NOTIFIER)
|
||||
void tcp_readahead_signal(FAR struct tcp_conn_s *conn);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tcp_writebuffer_signal
|
||||
*
|
||||
* Description:
|
||||
* All buffer Tx data has been sent. Signal all threads waiting for the
|
||||
* write buffers to become empty.
|
||||
*
|
||||
* When write buffer becomes empty, *all* of the workers waiting
|
||||
* for that event data will be executed. If there are multiple workers
|
||||
* waiting for read-ahead data then only the first to execute will get the
|
||||
* data. Others will need to call tcp_writebuffer_notifier_setup() once
|
||||
* again.
|
||||
*
|
||||
* Input Parameters:
|
||||
* conn - The TCP connection where read-ahead data was just buffered.
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#if defined(CONFIG_NET_TCP_WRITE_BUFFERS) && defined(CONFIG_TCP_NOTIFIER)
|
||||
void tcp_writebuffer_signal(FAR struct tcp_conn_s *conn);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tcp_disconnect_signal
|
||||
*
|
||||
@ -1679,6 +1737,30 @@ void tcp_readahead_signal(FAR struct tcp_conn_s *conn);
|
||||
void tcp_disconnect_signal(FAR struct tcp_conn_s *conn);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tcp_txdrain
|
||||
*
|
||||
* Description:
|
||||
* Wait for all write buffers to be sent (or for a timeout to occur).
|
||||
*
|
||||
* Input Parameters:
|
||||
* psock - An instance of the internal socket structure.
|
||||
* abstime - The absolute time when the timeout will occur
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) is returned on success; a negated errno value is returned
|
||||
* on any failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#if defined(CONFIG_NET_TCP_WRITE_BUFFERS) && defined(CONFIG_TCP_NOTIFIER)
|
||||
struct timespec;
|
||||
int tcp_txdrain(FAR struct socket *psock,
|
||||
FAR const struct timespec *abstime);
|
||||
#else
|
||||
# define udp_txdrain(conn, abstime) (0)
|
||||
#endif
|
||||
|
||||
#undef EXTERN
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
* net/tcp/tcp_notifier.c
|
||||
*
|
||||
* Copyright (C) 2018 Gregory Nutt. All rights reserved.
|
||||
* Copyright (C) 2018-2019 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -84,6 +84,7 @@ int tcp_readahead_notifier_setup(worker_t worker,
|
||||
FAR struct tcp_conn_s *conn,
|
||||
FAR void *arg)
|
||||
{
|
||||
#ifdef CONFIG_NET_TCP_READAHEAD
|
||||
struct work_notifier_s info;
|
||||
|
||||
DEBUGASSERT(worker != NULL);
|
||||
@ -106,10 +107,72 @@ int tcp_readahead_notifier_setup(worker_t worker,
|
||||
info.worker = worker;
|
||||
|
||||
return work_notifier_setup(&info);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tcp_readahead_disconnect_setup
|
||||
* Name: tcp_writebuffer_notifier_setup
|
||||
*
|
||||
* Description:
|
||||
* Set up to perform a callback to the worker function when an TCP write
|
||||
* buffer is emptied. The worker function will execute on the high
|
||||
* priority worker thread.
|
||||
*
|
||||
* Input Parameters:
|
||||
* worker - The worker function to execute on the high priority work
|
||||
* queue when all buffer TX data has been sent.
|
||||
* conn - The TCP connection where buffer write data is pending.
|
||||
* arg - A user-defined argument that will be available to the worker
|
||||
* function when it runs.
|
||||
*
|
||||
* Returned Value:
|
||||
* > 0 - The signal notification is in place. The returned value is a
|
||||
* key that may be used later in a call to
|
||||
* tcp_notifier_teardown().
|
||||
* == 0 - There is already buffered read-ahead data. No signal
|
||||
* notification will be provided.
|
||||
* < 0 - An unexpected error occurred and no signal will be sent. The
|
||||
* returned value is a negated errno value that indicates the
|
||||
* nature of the failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int tcp_writebuffer_notifier_setup(worker_t worker,
|
||||
FAR struct tcp_conn_s *conn,
|
||||
FAR void *arg)
|
||||
{
|
||||
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
|
||||
struct work_notifier_s info;
|
||||
|
||||
DEBUGASSERT(worker != NULL);
|
||||
|
||||
/* If the write buffers are already empty, then return zero without
|
||||
* setting up the notification.
|
||||
*/
|
||||
|
||||
if (sq_empty(&conn->write_q) && sq_empty(&conn->unacked_q))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Otherwise, this is just a simple wrapper around work_notifer_setup(). */
|
||||
|
||||
info.evtype = WORK_TCP_WRITEBUFFER;
|
||||
info.qid = LPWORK;
|
||||
info.qualifier = conn;
|
||||
info.arg = arg;
|
||||
info.worker = worker;
|
||||
|
||||
return work_notifier_setup(&info);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tcp_disconnect_notifier_setup
|
||||
*
|
||||
* Description:
|
||||
* Set up to perform a callback to the worker function if the TCP
|
||||
@ -133,9 +196,9 @@ int tcp_readahead_notifier_setup(worker_t worker,
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int tcp_readahead_disconnect_setup(worker_t worker,
|
||||
FAR struct tcp_conn_s *conn,
|
||||
FAR void *arg)
|
||||
int tcp_disconnect_notifier_setup(worker_t worker,
|
||||
FAR struct tcp_conn_s *conn,
|
||||
FAR void *arg)
|
||||
{
|
||||
struct work_notifier_s info;
|
||||
|
||||
@ -206,12 +269,44 @@ int tcp_notifier_teardown(int key)
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NET_TCP_READAHEAD
|
||||
void tcp_readahead_signal(FAR struct tcp_conn_s *conn)
|
||||
{
|
||||
/* This is just a simple wrapper around work_notifier_signal(). */
|
||||
|
||||
return work_notifier_signal(WORK_TCP_READAHEAD, conn);
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tcp_writebuffer_signal
|
||||
*
|
||||
* Description:
|
||||
* All buffer Tx data has been sent. Signal all threads waiting for the
|
||||
* write buffers to become empty.
|
||||
*
|
||||
* When write buffer becomes empty, *all* of the workers waiting
|
||||
* for that event data will be executed. If there are multiple workers
|
||||
* waiting for read-ahead data then only the first to execute will get the
|
||||
* data. Others will need to call tcp_writebuffer_notifier_setup() once
|
||||
* again.
|
||||
*
|
||||
* Input Parameters:
|
||||
* conn - The TCP connection where read-ahead data was just buffered.
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
|
||||
void tcp_writebuffer_signal(FAR struct tcp_conn_s *conn)
|
||||
{
|
||||
/* This is just a simple wrapper around work_notifier_signal(). */
|
||||
|
||||
return work_notifier_signal(WORK_TCP_WRITEBUFFER, conn);
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tcp_disconnect_signal
|
||||
|
@ -158,6 +158,37 @@ static void psock_insert_segment(FAR struct tcp_wrbuffer_s *wrb,
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: psock_writebuffer_notify
|
||||
*
|
||||
* Description:
|
||||
* The TCP connection has been lost. Free all write buffers.
|
||||
*
|
||||
* Input Parameters:
|
||||
* psock The socket structure
|
||||
* conn The connection structure associated with the socket
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_TCP_NOTIFIER
|
||||
static void psock_writebuffer_notify(FAR struct tcp_conn_s *conn)
|
||||
{
|
||||
/* Check if all write buffers have been sent and ACKed */
|
||||
|
||||
if (sq_empty(&conn->write_q) && sq_empty(&conn->unacked_q))
|
||||
{
|
||||
/* Notify any waiters that the write buffers have been drained. */
|
||||
|
||||
tcp_writebuffer_signal(conn);
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define psock_writebuffer_notify(conn)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: psock_lost_connection
|
||||
*
|
||||
@ -207,6 +238,11 @@ static inline void psock_lost_connection(FAR struct socket *psock,
|
||||
|
||||
sq_init(&conn->unacked_q);
|
||||
sq_init(&conn->write_q);
|
||||
|
||||
/* Notify any waiters if the write buffers have been drained. */
|
||||
|
||||
psock_writebuffer_notify(conn);
|
||||
|
||||
conn->sent = 0;
|
||||
conn->sndseq_max = 0;
|
||||
}
|
||||
@ -508,6 +544,12 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev,
|
||||
/* And return the write buffer to the pool of free buffers */
|
||||
|
||||
tcp_wrbuffer_release(wrb);
|
||||
|
||||
/* Notify any waiters if the write buffers have been
|
||||
* drained.
|
||||
*/
|
||||
|
||||
psock_writebuffer_notify(conn);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -664,6 +706,12 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev,
|
||||
|
||||
tcp_wrbuffer_release(wrb);
|
||||
|
||||
/* Notify any waiters if the write buffers have been
|
||||
* drained.
|
||||
*/
|
||||
|
||||
psock_writebuffer_notify(conn);
|
||||
|
||||
/* NOTE expired is different from un-ACKed, it is designed to
|
||||
* represent the number of segments that have been sent,
|
||||
* retransmitted, and un-ACKed, if expired is not zero, the
|
||||
@ -722,6 +770,12 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev,
|
||||
|
||||
tcp_wrbuffer_release(wrb);
|
||||
|
||||
/* Notify any waiters if the write buffers have been
|
||||
* drained.
|
||||
*/
|
||||
|
||||
psock_writebuffer_notify(conn);
|
||||
|
||||
/* NOTE expired is different from un-ACKed, it is designed to
|
||||
* represent the number of segments that have been sent,
|
||||
* retransmitted, and un-ACKed, if expired is not zero, the
|
||||
|
146
net/tcp/tcp_txdrain.c
Normal file
146
net/tcp/tcp_txdrain.c
Normal file
@ -0,0 +1,146 @@
|
||||
/****************************************************************************
|
||||
* net/tcp/tcp_txdrain.c
|
||||
*
|
||||
* Copyright (C) 2019 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name NuttX nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <semaphore.h>
|
||||
#include <sched.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/wqueue.h>
|
||||
#include <nuttx/net/net.h>
|
||||
|
||||
#include "tcp/tcp.h"
|
||||
|
||||
#if defined(CONFIG_NET_TCP_WRITE_BUFFERS) && defined(CONFIG_TCP_NOTIFIER)
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: txdrain_worker
|
||||
*
|
||||
* Description:
|
||||
* Called with the write buffers have all been sent.
|
||||
*
|
||||
* Input Parameters:
|
||||
* arg - The semaphore that will wake up tcp_txdrain
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void txdrain_worker(FAR void *arg)
|
||||
{
|
||||
FAR sem_t *waitsem = (FAR sem_t *)arg;
|
||||
DEBUGASSERT(waitsem != NULL);
|
||||
sem_post(waitsem);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tcp_txdrain
|
||||
*
|
||||
* Description:
|
||||
* Wait for all write buffers to be sent (or for a timeout to occur).
|
||||
*
|
||||
* Input Parameters:
|
||||
* psock - An instance of the internal socket structure.
|
||||
* abstime - The absolute time when the timeout will occur
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) is returned on success; a negated errno value is returned
|
||||
* on any failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int tcp_txdrain(FAR struct socket *psock,
|
||||
FAR const struct timespec *abstime)
|
||||
{
|
||||
FAR struct tcp_conn_s *conn;
|
||||
sem_t waitsem;
|
||||
int ret;
|
||||
|
||||
DEBUGASSERT(psock != NULL && psock->s_crefs > 0 && psock->s_conn != NULL);
|
||||
DEBUGASSERT(psock->s_type == SOCK_DGRAM);
|
||||
|
||||
conn = (FAR struct tcp_conn_s *)psock->s_conn;
|
||||
|
||||
/* Initialize the wait semaphore */
|
||||
|
||||
nxsem_init(&waitsem, 0, 0);
|
||||
|
||||
/* The following needs to be done with the network stable */
|
||||
|
||||
net_lock();
|
||||
ret = tcp_writebuffer_notifier_setup(txdrain_worker, conn, &waitsem);
|
||||
if (ret > 0)
|
||||
{
|
||||
int key = ret;
|
||||
|
||||
/* There is pending write data.. wait for it to drain. */
|
||||
|
||||
do
|
||||
{
|
||||
ret = net_timedwait(&waitsem, abstime);
|
||||
}
|
||||
while (ret == EINTR);
|
||||
|
||||
/* Tear down the notifier (in case we timed out or were canceled) */
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
tcp_notifier_teardown(key);
|
||||
}
|
||||
}
|
||||
|
||||
net_unlock();
|
||||
nxsem_destroy(&waitsem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NET_TCP_WRITE_BUFFERS && CONFIG_TCP_NOTIFIER */
|
@ -57,18 +57,6 @@ config NET_UDP_READAHEAD
|
||||
select NET_READAHEAD
|
||||
select MM_IOB
|
||||
|
||||
config UDP_READAHEAD_NOTIFIER
|
||||
bool "Support UDP read-ahead notifications"
|
||||
default n
|
||||
depends on NET_UDP_READAHEAD && SCHED_WORKQUEUE
|
||||
select WQUEUE_NOTIFIER
|
||||
---help---
|
||||
Enable building of UDP read-ahead notifier logic that will execute a
|
||||
worker function on the high priority work queue when read-ahead data
|
||||
is available. This is is a general purpose notifier, but was
|
||||
developed specifically to support poll() logic where the poll must
|
||||
wait for read-ahead data to become available.
|
||||
|
||||
config NET_UDP_WRITE_BUFFERS
|
||||
bool "Enable UDP/IP write buffering"
|
||||
default n
|
||||
@ -117,5 +105,17 @@ config NET_UDP_WRBUFFER_DUMP
|
||||
|
||||
endif # NET_UDP_WRITE_BUFFERS
|
||||
|
||||
config UDP_NOTIFIER
|
||||
bool "Support UDP read-ahead notifications"
|
||||
default n
|
||||
depends on SCHED_WORKQUEUE
|
||||
select WQUEUE_NOTIFIER
|
||||
---help---
|
||||
Enable building of UDP read-ahead notifier logic that will execute a
|
||||
worker function on the high priority work queue when read-ahead data
|
||||
is available. This is is a general purpose notifier, but was
|
||||
developed specifically to support poll() logic where the poll must
|
||||
wait for read-ahead data to become available.
|
||||
|
||||
endif # NET_UDP && !NET_UDP_NO_STACK
|
||||
endmenu # UDP Networking
|
||||
|
@ -54,9 +54,12 @@ endif
|
||||
|
||||
ifeq ($(CONFIG_NET_UDP_READAHEAD),y)
|
||||
SOCK_CSRCS += udp_netpoll.c
|
||||
ifeq ($(CONFIG_UDP_READAHEAD_NOTIFIER),y)
|
||||
ifeq ($(CONFIG_UDP_NOTIFIER),y)
|
||||
SOCK_CSRCS += udp_notifier.c
|
||||
endif
|
||||
ifeq ($(CONFIG_NET_UDP_WRITE_BUFFERS),y)
|
||||
SOCK_CSRCS += udp_txdrain.c
|
||||
endif
|
||||
endif
|
||||
|
||||
# Transport layer
|
||||
|
106
net/udp/udp.h
106
net/udp/udp.h
@ -53,7 +53,7 @@
|
||||
# include <nuttx/mm/iob.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_UDP_READAHEAD_NOTIFIER
|
||||
#ifdef CONFIG_UDP_NOTIFIER
|
||||
# include <nuttx/wqueue.h>
|
||||
#endif
|
||||
|
||||
@ -671,7 +671,7 @@ int udp_pollteardown(FAR struct socket *psock, FAR struct pollfd *fds);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: udp_notifier_setup
|
||||
* Name: udp_readahead_notifier_setup
|
||||
*
|
||||
* Description:
|
||||
* Set up to perform a callback to the worker function when an UDP data
|
||||
@ -696,9 +696,42 @@ int udp_pollteardown(FAR struct socket *psock, FAR struct pollfd *fds);
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_UDP_READAHEAD_NOTIFIER
|
||||
int udp_notifier_setup(worker_t worker, FAR struct udp_conn_s *conn,
|
||||
FAR void *arg);
|
||||
#ifdef CONFIG_UDP_NOTIFIER
|
||||
int udp_readahead_notifier_setup(worker_t worker,
|
||||
FAR struct udp_conn_s *conn,
|
||||
FAR void *arg);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: udp_writebuffer_notifier_setup
|
||||
*
|
||||
* Description:
|
||||
* Set up to perform a callback to the worker function when an UDP write
|
||||
* buffer is emptied. The worker function will execute on the high
|
||||
* priority worker thread.
|
||||
*
|
||||
* Input Parameters:
|
||||
* worker - The worker function to execute on the low priority work
|
||||
* queue when data is available in the UDP read-ahead buffer.
|
||||
* conn - The UDP connection where read-ahead data is needed.
|
||||
* arg - A user-defined argument that will be available to the worker
|
||||
* function when it runs.
|
||||
*
|
||||
* Returned Value:
|
||||
* > 0 - The notification is in place. The returned value is a key that
|
||||
* may be used later in a call to udp_notifier_teardown().
|
||||
* == 0 - There is already buffered read-ahead data. No notification
|
||||
* will be provided.
|
||||
* < 0 - An unexpected error occurred and no notification will occur.
|
||||
* The returned value is a negated errno value that indicates the
|
||||
* nature of the failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_UDP_NOTIFIER
|
||||
int udp_writebuffer_notifier_setup(worker_t worker,
|
||||
FAR struct udp_conn_s *conn,
|
||||
FAR void *arg);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
@ -706,13 +739,13 @@ int udp_notifier_setup(worker_t worker, FAR struct udp_conn_s *conn,
|
||||
*
|
||||
* Description:
|
||||
* Eliminate a UDP read-ahead notification previously setup by
|
||||
* udp_notifier_setup(). This function should only be called if the
|
||||
* udp_readahead_notifier_setup(). This function should only be called if the
|
||||
* notification should be aborted prior to the notification. The
|
||||
* notification will automatically be torn down after the notification.
|
||||
*
|
||||
* Input Parameters:
|
||||
* key - The key value returned from a previous call to
|
||||
* udp_notifier_setup().
|
||||
* udp_readahead_notifier_setup().
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) is returned on success; a negated errno value is returned on
|
||||
@ -720,12 +753,12 @@ int udp_notifier_setup(worker_t worker, FAR struct udp_conn_s *conn,
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_UDP_READAHEAD_NOTIFIER
|
||||
#ifdef CONFIG_UDP_NOTIFIER
|
||||
int udp_notifier_teardown(int key);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: udp_notifier_signal
|
||||
* Name: udp_readahead_signal
|
||||
*
|
||||
* Description:
|
||||
* Read-ahead data has been buffered. Notify all threads waiting for
|
||||
@ -734,7 +767,7 @@ int udp_notifier_teardown(int key);
|
||||
* When read-ahead data becomes available, *all* of the workers waiting
|
||||
* for read-ahead data will be executed. If there are multiple workers
|
||||
* waiting for read-ahead data then only the first to execute will get the
|
||||
* data. Others will need to call udp_notifier_setup() once again.
|
||||
* data. Others will need to call udp_readahead_notifier_setup() once again.
|
||||
*
|
||||
* Input Parameters:
|
||||
* conn - The UDP connection where read-ahead data was just buffered.
|
||||
@ -744,8 +777,57 @@ int udp_notifier_teardown(int key);
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_UDP_READAHEAD_NOTIFIER
|
||||
void udp_notifier_signal(FAR struct udp_conn_s *conn);
|
||||
#if defined(CONFIG_NET_UDP_READAHEAD) && defined(CONFIG_UDP_NOTIFIER)
|
||||
void udp_readahead_signal(FAR struct udp_conn_s *conn);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: udp_writebuffer_signal
|
||||
*
|
||||
* Description:
|
||||
* All buffer Tx data has been sent. Signal all threads waiting for the
|
||||
* write buffers to become empty.
|
||||
*
|
||||
* When write buffer becomes empty, *all* of the workers waiting
|
||||
* for that event data will be executed. If there are multiple workers
|
||||
* waiting for read-ahead data then only the first to execute will get the
|
||||
* data. Others will need to call tcp_writebuffer_notifier_setup() once
|
||||
* again.
|
||||
*
|
||||
* Input Parameters:
|
||||
* conn - The UDP connection where read-ahead data was just buffered.
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#if defined(CONFIG_NET_UDP_WRITE_BUFFERS) && defined(CONFIG_UDP_NOTIFIER)
|
||||
void udp_writebuffer_signal(FAR struct udp_conn_s *conn);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: udp_txdrain
|
||||
*
|
||||
* Description:
|
||||
* Wait for all write buffers to be sent (or for a timeout to occur).
|
||||
*
|
||||
* Input Parameters:
|
||||
* psock - An instance of the internal socket structure.
|
||||
* abstime - The absolute time when the timeout will occur
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) is returned on success; a negated errno value is returned
|
||||
* on any failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#if defined(CONFIG_NET_UDP_WRITE_BUFFERS) && defined(CONFIG_UDP_NOTIFIER)
|
||||
struct timespec;
|
||||
int udp_txdrain(FAR struct socket *psock,
|
||||
FAR const struct timespec *abstime);
|
||||
#else
|
||||
# define udp_txdrain(conn, abstime) (0)
|
||||
#endif
|
||||
|
||||
#undef EXTERN
|
||||
|
@ -230,12 +230,12 @@ static uint16_t udp_datahandler(FAR struct net_driver_s *dev, FAR struct udp_con
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_UDP_READAHEAD_NOTIFIER
|
||||
#ifdef CONFIG_UDP_NOTIFIER
|
||||
/* Provided notification(s) that additional UDP read-ahead data is
|
||||
* available.
|
||||
*/
|
||||
|
||||
udp_notifier_signal(conn);
|
||||
udp_readahead_signal(conn);
|
||||
#endif
|
||||
|
||||
ninfo("Buffered %d bytes\n", buflen);
|
||||
|
@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
* net/udp/udp_notifier.c
|
||||
*
|
||||
* Copyright (C) 2018 Gregory Nutt. All rights reserved.
|
||||
* Copyright (C) 2018-2019 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -47,14 +47,14 @@
|
||||
|
||||
#include "udp/udp.h"
|
||||
|
||||
#ifdef CONFIG_UDP_READAHEAD_NOTIFIER
|
||||
#ifdef CONFIG_UDP_NOTIFIER
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: udp_notifier_setup
|
||||
* Name: udp_readahead_notifier_setup
|
||||
*
|
||||
* Description:
|
||||
* Set up to perform a callback to the worker function when an UDP data
|
||||
@ -79,9 +79,11 @@
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int udp_notifier_setup(worker_t worker, FAR struct udp_conn_s *conn,
|
||||
FAR void *arg)
|
||||
int udp_readahead_notifier_setup(worker_t worker,
|
||||
FAR struct udp_conn_s *conn,
|
||||
FAR void *arg)
|
||||
{
|
||||
#ifdef CONFIG_NET_UDP_READAHEAD
|
||||
struct work_notifier_s info;
|
||||
|
||||
DEBUGASSERT(worker != NULL);
|
||||
@ -104,6 +106,67 @@ int udp_notifier_setup(worker_t worker, FAR struct udp_conn_s *conn,
|
||||
info.worker = worker;
|
||||
|
||||
return work_notifier_setup(&info);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: udp_writebuffer_notifier_setup
|
||||
*
|
||||
* Description:
|
||||
* Set up to perform a callback to the worker function when an UDP write
|
||||
* buffer is emptied. The worker function will execute on the high
|
||||
* priority worker thread.
|
||||
*
|
||||
* Input Parameters:
|
||||
* worker - The worker function to execute on the low priority work
|
||||
* queue when data is available in the UDP read-ahead buffer.
|
||||
* conn - The UDP connection where read-ahead data is needed.
|
||||
* arg - A user-defined argument that will be available to the worker
|
||||
* function when it runs.
|
||||
*
|
||||
* Returned Value:
|
||||
* > 0 - The notification is in place. The returned value is a key that
|
||||
* may be used later in a call to udp_notifier_teardown().
|
||||
* == 0 - There is already buffered read-ahead data. No notification
|
||||
* will be provided.
|
||||
* < 0 - An unexpected error occurred and no notification will occur.
|
||||
* The returned value is a negated errno value that indicates the
|
||||
* nature of the failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int udp_writebuffer_notifier_setup(worker_t worker,
|
||||
FAR struct udp_conn_s *conn,
|
||||
FAR void *arg)
|
||||
{
|
||||
#ifdef CONFIG_NET_UDP_WRITE_BUFFERS
|
||||
struct work_notifier_s info;
|
||||
|
||||
DEBUGASSERT(worker != NULL);
|
||||
|
||||
/* If there is already buffered read-ahead data, then return zero without
|
||||
* setting up the notification.
|
||||
*/
|
||||
|
||||
if (sq_empty(&conn->write_q))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Otherwise, this is just a simple wrapper around work_notifer_setup(). */
|
||||
|
||||
info.evtype = WORK_UDP_WRITEBUFFER;
|
||||
info.qid = LPWORK;
|
||||
info.qualifier = conn;
|
||||
info.arg = arg;
|
||||
info.worker = worker;
|
||||
|
||||
return work_notifier_setup(&info);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -111,13 +174,13 @@ int udp_notifier_setup(worker_t worker, FAR struct udp_conn_s *conn,
|
||||
*
|
||||
* Description:
|
||||
* Eliminate a UDP read-ahead notification previously setup by
|
||||
* udp_notifier_setup(). This function should only be called if the
|
||||
* udp_readahead_notifier_setup(). This function should only be called if the
|
||||
* notification should be aborted prior to the notification. The
|
||||
* notification will automatically be torn down after the notification.
|
||||
*
|
||||
* Input Parameters:
|
||||
* key - The key value returned from a previous call to
|
||||
* udp_notifier_setup().
|
||||
* udp_readahead_notifier_setup().
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) is returned on success; a negated errno value is returned on
|
||||
@ -133,7 +196,7 @@ int udp_notifier_teardown(int key)
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: udp_notifier_signal
|
||||
* Name: udp_readahead_signal
|
||||
*
|
||||
* Description:
|
||||
* Read-ahead data has been buffered. Notify all threads waiting for
|
||||
@ -142,7 +205,7 @@ int udp_notifier_teardown(int key)
|
||||
* When read-ahead data becomes available, *all* of the workers waiting
|
||||
* for read-ahead data will be executed. If there are multiple workers
|
||||
* waiting for read-ahead data then only the first to execute will get the
|
||||
* data. Others will need to call udp_notifier_setup() once again.
|
||||
* data. Others will need to call udp_readahead_notifier_setup() once again.
|
||||
*
|
||||
* Input Parameters:
|
||||
* conn - The UDP connection where read-ahead data was just buffered.
|
||||
@ -152,11 +215,43 @@ int udp_notifier_teardown(int key)
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void udp_notifier_signal(FAR struct udp_conn_s *conn)
|
||||
#ifdef CONFIG_NET_UDP_READAHEAD
|
||||
void udp_readahead_signal(FAR struct udp_conn_s *conn)
|
||||
{
|
||||
/* This is just a simple wrapper around work_notifier_signal(). */
|
||||
|
||||
return work_notifier_signal(WORK_UDP_READAHEAD, conn);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_UDP_READAHEAD_NOTIFIER */
|
||||
/****************************************************************************
|
||||
* Name: udp_writebuffer_signal
|
||||
*
|
||||
* Description:
|
||||
* All buffer Tx data has been sent. Signal all threads waiting for the
|
||||
* write buffers to become empty.
|
||||
*
|
||||
* When write buffer becomes empty, *all* of the workers waiting
|
||||
* for that event data will be executed. If there are multiple workers
|
||||
* waiting for read-ahead data then only the first to execute will get the
|
||||
* data. Others will need to call tcp_writebuffer_notifier_setup() once
|
||||
* again.
|
||||
*
|
||||
* Input Parameters:
|
||||
* conn - The UDP connection where read-ahead data was just buffered.
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NET_UDP_WRITE_BUFFERS
|
||||
void udp_writebuffer_signal(FAR struct udp_conn_s *conn)
|
||||
{
|
||||
/* This is just a simple wrapper around work_notifier_signal(). */
|
||||
|
||||
return work_notifier_signal(WORK_UDP_WRITEBUFFER, conn);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_UDP_NOTIFIER */
|
||||
|
@ -167,6 +167,12 @@ static void sendto_writebuffer_release(FAR struct socket *psock,
|
||||
psock->s_sndcb->priv = NULL;
|
||||
psock->s_sndcb->event = NULL;
|
||||
wrb = NULL;
|
||||
|
||||
#ifdef CONFIG_TCP_NOTIFIER
|
||||
/* Notify any waiters that the write buffers have been drained. */
|
||||
|
||||
udp_writebuffer_signal(conn);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
146
net/udp/udp_txdrain.c
Normal file
146
net/udp/udp_txdrain.c
Normal file
@ -0,0 +1,146 @@
|
||||
/****************************************************************************
|
||||
* net/udp/udp_txdrain.c
|
||||
*
|
||||
* Copyright (C) 2019 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name NuttX nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <semaphore.h>
|
||||
#include <sched.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/wqueue.h>
|
||||
#include <nuttx/net/net.h>
|
||||
|
||||
#include "udp/udp.h"
|
||||
|
||||
#if defined(CONFIG_NET_UDP_WRITE_BUFFERS) && defined(CONFIG_UDP_NOTIFIER)
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: txdrain_worker
|
||||
*
|
||||
* Description:
|
||||
* Called with the write buffers have all been sent.
|
||||
*
|
||||
* Input Parameters:
|
||||
* arg - The semaphore that will wake up udp_txdrain
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void txdrain_worker(FAR void *arg)
|
||||
{
|
||||
FAR sem_t *waitsem = (FAR sem_t *)arg;
|
||||
DEBUGASSERT(waitsem != NULL);
|
||||
sem_post(waitsem);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: udp_txdrain
|
||||
*
|
||||
* Description:
|
||||
* Wait for all write buffers to be sent (or for a timeout to occur).
|
||||
*
|
||||
* Input Parameters:
|
||||
* psock - An instance of the internal socket structure.
|
||||
* abstime - The absolute time when the timeout will occur
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) is returned on success; a negated errno value is returned
|
||||
* on any failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int udp_txdrain(FAR struct socket *psock,
|
||||
FAR const struct timespec *abstime)
|
||||
{
|
||||
FAR struct udp_conn_s *conn;
|
||||
sem_t waitsem;
|
||||
int ret;
|
||||
|
||||
DEBUGASSERT(psock != NULL && psock->s_crefs > 0 && psock->s_conn != NULL);
|
||||
DEBUGASSERT(psock->s_type == SOCK_DGRAM);
|
||||
|
||||
conn = (FAR struct udp_conn_s *)psock->s_conn;
|
||||
|
||||
/* Initialize the wait semaphore */
|
||||
|
||||
nxsem_init(&waitsem, 0, 0);
|
||||
|
||||
/* The following needs to be done with the network stable */
|
||||
|
||||
net_lock();
|
||||
ret = udp_writebuffer_notifier_setup(txdrain_worker, conn, &waitsem);
|
||||
if (ret > 0)
|
||||
{
|
||||
int key = ret;
|
||||
|
||||
/* There is pending write data.. wait for it to drain. */
|
||||
|
||||
do
|
||||
{
|
||||
ret = net_timedwait(&waitsem, abstime);
|
||||
}
|
||||
while (ret == EINTR);
|
||||
|
||||
/* Tear down the notifier (in case we timed out or were canceled) */
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
udp_notifier_teardown(key);
|
||||
}
|
||||
}
|
||||
|
||||
net_unlock();
|
||||
nxsem_destroy(&waitsem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NET_UDP_WRITE_BUFFERS && CONFIG_UDP_NOTIFIER */
|
Loading…
Reference in New Issue
Block a user