net/udp/udp_netpoll.c: Port TCP poll changes to UDP.

This commit is contained in:
Gregory Nutt 2018-09-12 10:10:54 -06:00
parent b3f0aab00a
commit 6e60af91e4
5 changed files with 218 additions and 35 deletions

View File

@ -67,7 +67,7 @@ struct tcp_poll_s
FAR struct socket *psock; /* Needed to handle loss of connection */
struct pollfd *fds; /* Needed to handle poll events */
FAR struct devif_callback_s *cb; /* Needed to teardown the poll */
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
#if defined(CONFIG_NET_TCP_WRITE_BUFFERS) && defined(CONFIG_IOB_NOTIFIER)
int16_t key; /* Needed to cancel pending notification */
#endif
};
@ -197,34 +197,42 @@ static inline void tcp_iob_work(FAR void *arg)
if (!_SS_ISCONNECTED(psock->s_flags) && !_SS_ISLISTENING(psock->s_flags))
{
/* We were previously connected but lost the connection either due
* to a graceful shutdown by the remote peer or because of some
* exceptional event.
*/
/* Don't report more than once. Might happen in a race condition */
fds->revents |= (POLLERR | POLLHUP);
if ((fds->revents & (POLLERR | POLLHUP)) == 0)
{
/* We were previously connected but lost the connection either due
* to a graceful shutdown by the remote peer or because of some
* exceptional event.
*/
fds->revents |= (POLLERR | POLLHUP);
nxsem_post(fds->sem);
}
}
/* Check if we are now able to send */
/* Handle a race condition. Check if we have already posted the POLLOUT
* event. If so, don't do it again and don't setup notification again.
*/
else if (_SS_ISCONNECTED(psock->s_flags) && psock_tcp_cansend(psock) >= 0)
if ((fds->events && POLLWRNORM) == 0 ||
(fds->revents && POLLWRNORM) != 0)
{
fds->revents |= (POLLWRNORM & fds->events);
}
/* Check if we are now able to send */
/* Check if any requested events are already in effect */
if (psock_tcp_cansend(psock) >= 0)
{
/* Yes.. then signal the poll logic */
if (fds->revents != 0)
{
/* Yes.. then signal the poll logic */
fds->revents |= POLLWRNORM;
nxsem_post(fds->sem);
}
else
{
/* No.. ask for the IOB free notification again */
nxsem_post(fds->sem);
}
else
{
/* No.. ask for the IOB free notification again */
pinfo->key = iob_notifier_setup(LPWORK, tcp_iob_work, pinfo);
pinfo->key = iob_notifier_setup(LPWORK, tcp_iob_work, pinfo);
}
}
/* Protocol for the use of the IOB notifier is that we free the argument
@ -240,7 +248,7 @@ static inline void tcp_iob_work(FAR void *arg)
*
* Description:
* Notify the appropriate device driver that we are have data ready to
* be send (TCP)
* be sent (TCP)
*
* Input Parameters:
* psock - Socket state structure
@ -250,7 +258,7 @@ static inline void tcp_iob_work(FAR void *arg)
*
****************************************************************************/
#ifndef CONFIG_NET_TCP_WRITE_BUFFERS
#if !defined(CONFIG_NET_TCP_WRITE_BUFFERS) || !defined(CONFIG_IOB_NOTIFIER)
static inline void tcp_poll_txnotify(FAR struct socket *psock)
{
FAR struct tcp_conn_s *conn = psock->s_conn;
@ -346,7 +354,7 @@ int tcp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds)
info->psock = psock;
info->fds = fds;
info->cb = cb;
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
#if defined(CONFIG_NET_TCP_WRITE_BUFFERS) && defined(CONFIG_IOB_NOTIFIER)
info->key = 0;
#endif
@ -519,7 +527,7 @@ int tcp_pollteardown(FAR struct socket *psock, FAR struct pollfd *fds)
DEBUGASSERT(info != NULL && info->fds != NULL && info->cb != NULL);
if (info != NULL)
{
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
#if defined(CONFIG_NET_TCP_WRITE_BUFFERS) && defined(CONFIG_IOB_NOTIFIER)
/* Cancel any pending IOB free notification */
if (info->key > 0)

View File

@ -964,7 +964,7 @@ errout:
* psock An instance of the internal socket structure.
*
* Returned Value:
* -ENOSYS (Function not implemented).
* -ENOSYS (Function not implemented, always have to wait to send).
*
* Assumptions:
* None
@ -973,8 +973,6 @@ errout:
int psock_tcp_cansend(FAR struct socket *psock)
{
/* TODO: return OK unless someone is waiting for a packet to send */
return -ENOSYS;
}

View File

@ -45,6 +45,8 @@
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/wqueue.h>
#include <nuttx/mm/iob.h>
#include <nuttx/net/net.h>
#include "devif/devif.h"
@ -65,6 +67,9 @@ struct udp_poll_s
FAR struct net_driver_s *dev; /* Needed to free the callback structure */
struct pollfd *fds; /* Needed to handle poll events */
FAR struct devif_callback_s *cb; /* Needed to teardown the poll */
#if defined(CONFIG_NET_UDP_WRITE_BUFFERS) && defined(CONFIG_IOB_NOTIFIER)
int16_t key; /* Needed to cancel pending notification */
#endif
};
/****************************************************************************
@ -145,11 +150,10 @@ static uint16_t udp_poll_eventhandler(FAR struct net_driver_s *dev,
}
/****************************************************************************
* Name: udp_poll_txnotify
* Name: udp_iob_work
*
* Description:
* Notify the appropriate device driver that we are have data ready to
* be send (TCP)
* Work thread callback function execute when an IOB because available.
*
* Input Parameters:
* psock - Socket state structure
@ -159,7 +163,75 @@ static uint16_t udp_poll_eventhandler(FAR struct net_driver_s *dev,
*
****************************************************************************/
#ifndef CONFIG_NET_UDP_WRITE_BUFFERS
#if defined(CONFIG_NET_UDP_WRITE_BUFFERS) && defined(CONFIG_IOB_NOTIFIER)
static inline void udp_iob_work(FAR void *arg)
{
FAR struct work_notifier_entry_s *entry;
FAR struct work_notifier_s *ninfo;
FAR struct udp_poll_s *pinfo;
FAR struct socket *psock;
FAR struct pollfd *fds;
entry = (FAR struct work_notifier_entry_s *)arg;
DEBUGASSERT(entry != NULL);
ninfo = &entry->info;
DEBUGASSERT(ninfo->arg != NULL);
pinfo = (FAR struct udp_poll_s *)ninfo->arg;
DEBUGASSERT(pinfo->psock != NULL && pinfo->fds != NULL);
psock = pinfo->psock;
fds = pinfo->fds;
/* Handle a race condition. Check if we have already posted the POLLOUT
* event. If so, don't do it again.
*/
if ((fds->events && POLLWRNORM) == 0 ||
(fds->revents && POLLWRNORM) != 0)
{
/* Check if we are now able to send */
if (psock_udp_cansend(psock) >= 0)
{
/* Yes.. then signal the poll logic */
fds->revents |= POLLWRNORM;
nxsem_post(fds->sem);
}
else
{
/* No.. ask for the IOB free notification again */
pinfo->key = iob_notifier_setup(LPWORK, udp_iob_work, pinfo);
}
}
/* Protocol for the use of the IOB notifier is that we free the argument
* after the notification has been processed.
*/
kmm_free(arg);
}
#endif
/****************************************************************************
* Name: udp_poll_txnotify
*
* Description:
* Notify the appropriate device driver that we are have data ready to
* be sent (UDP)
*
* Input Parameters:
* psock - Socket state structure
*
* Returned Value:
* None
*
****************************************************************************/
#if !defined(CONFIG_NET_UDP_WRITE_BUFFERS) || !defined(CONFIG_IOB_NOTIFIER)
static inline void udp_poll_txnotify(FAR struct socket *psock)
{
FAR struct udp_conn_s *conn = psock->s_conn;
@ -262,6 +334,9 @@ int udp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds)
info->psock = psock;
info->fds = fds;
info->cb = cb;
#if defined(CONFIG_NET_UDP_WRITE_BUFFERS) && defined(CONFIG_IOB_NOTIFIER)
info->key = 0;
#endif
/* Initialize the callback structure. Save the reference to the info
* structure as callback private data so that it will be available during
@ -302,6 +377,13 @@ int udp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds)
fds->revents |= (POLLRDNORM & fds->events);
}
if (psock_udp_cansend(psock) >= 0)
{
/* Normal data may be sent without blocking (at least one byte). */
fds->revents |= (POLLWRNORM & fds->events);
}
/* Check if any requested events are already in effect */
if (fds->revents != 0)
@ -311,7 +393,20 @@ int udp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds)
nxsem_post(fds->sem);
}
#ifndef CONFIG_NET_UDP_WRITE_BUFFERS
#if defined(CONFIG_NET_UDP_WRITE_BUFFERS) && defined(CONFIG_IOB_NOTIFIER)
/* If (1) revents == 0, (2) write buffering is enabled, and (3) the
* POLLOUT event is needed, then setup to receive a notification an IOB
* is freed.
*/
else if ((fds->events & POLLOUT) != 0)
{
/* Ask for the IOB free notification */
info->key = iob_notifier_setup(LPWORK, udp_iob_work, info);
}
#else
/* If (1) the socket is in a bound state via bind() or via the
* UDP_BINDTODEVICE socket options, (2) revents == 0, (3) write buffering
* is not enabled (determined by a configuration setting), and (3) the
@ -394,9 +489,20 @@ int udp_pollteardown(FAR struct socket *psock, FAR struct pollfd *fds)
/* Recover the socket descriptor poll state info from the poll structure */
info = (FAR struct udp_poll_s *)fds->priv;
DEBUGASSERT(info && info->fds && info->cb);
if (info)
DEBUGASSERT(info != NULL && info->fds != NULL && info->cb != NULL);
if (info != NULL)
{
#if defined(CONFIG_NET_UDP_WRITE_BUFFERS) && defined(CONFIG_IOB_NOTIFIER)
/* Cancel any pending IOB free notification */
if (info->key > 0)
{
/* Ask for the IOB free notification */
iob_notifier_teardown(info->key);
}
#endif
/* Release the callback */
net_lock();

View File

@ -881,4 +881,51 @@ errout_with_lock:
return ret;
}
/****************************************************************************
* Name: psock_udp_cansend
*
* Description:
* psock_udp_cansend() returns a value indicating if a write to the socket
* would block. No space in the buffer is actually reserved, so it is
* possible that the write may still block if the buffer is filled by
* another means.
*
* Input Parameters:
* psock An instance of the internal socket structure.
*
* Returned Value:
* OK
* At least one byte of data could be successfully written.
* -EWOULDBLOCK
* There is no room in the output buffer.
* -EBADF
* An invalid descriptor was specified.
*
****************************************************************************/
int psock_udp_cansend(FAR struct socket *psock)
{
/* Verify that we received a valid socket */
if (!psock || psock->s_crefs <= 0)
{
nerr("ERROR: Invalid socket\n");
return -EBADF;
}
/* In order to setup the send, we need to have at least one free write
* buffer head and at least one free IOB to initialize the write buffer head.
*
* REVISIT: The send will still block if we are unable to buffer the entire
* user-provided buffer which may be quite large. We will almost certainly
* need to have more than one free IOB, but we don't know how many more.
*/
if (udp_wrbuffer_test() < 0 || iob_navail(false) <= 0)
{
return -EWOULDBLOCK;
}
return OK;
}
#endif /* CONFIG_NET && CONFIG_NET_UDP && CONFIG_NET_UDP_WRITE_BUFFERS */

View File

@ -562,4 +562,28 @@ errout_with_lock:
return ret;
}
/****************************************************************************
* Name: psock_udp_cansend
*
* Description:
* psock_udp_cansend() returns a value indicating if a write to the socket
* would block. It is still possible that the write may block if another
* write occurs first.
*
* Input Parameters:
* psock An instance of the internal socket structure.
*
* Returned Value:
* -ENOSYS (Function not implemented, always have to wait to send).
*
* Assumptions:
* None
*
****************************************************************************/
int psock_udp_cansend(FAR struct socket *psock)
{
return -ENOSYS;
}
#endif /* CONFIG_NET_UDP */