diff --git a/net/tcp/tcp_netpoll.c b/net/tcp/tcp_netpoll.c index aa32df6fc5..599d6932f8 100644 --- a/net/tcp/tcp_netpoll.c +++ b/net/tcp/tcp_netpoll.c @@ -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) diff --git a/net/tcp/tcp_send_unbuffered.c b/net/tcp/tcp_send_unbuffered.c index 5b285090d2..47720b6521 100644 --- a/net/tcp/tcp_send_unbuffered.c +++ b/net/tcp/tcp_send_unbuffered.c @@ -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; } diff --git a/net/udp/udp_netpoll.c b/net/udp/udp_netpoll.c index f0e51b641f..245e641969 100644 --- a/net/udp/udp_netpoll.c +++ b/net/udp/udp_netpoll.c @@ -45,6 +45,8 @@ #include #include +#include +#include #include #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(); diff --git a/net/udp/udp_psock_sendto_buffered.c b/net/udp/udp_psock_sendto_buffered.c index 541aa84dea..6d209c9402 100644 --- a/net/udp/udp_psock_sendto_buffered.c +++ b/net/udp/udp_psock_sendto_buffered.c @@ -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 */ diff --git a/net/udp/udp_psock_sendto_unbuffered.c b/net/udp/udp_psock_sendto_unbuffered.c index af0b1e2ee9..1d15101ab4 100644 --- a/net/udp/udp_psock_sendto_unbuffered.c +++ b/net/udp/udp_psock_sendto_unbuffered.c @@ -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 */