net/udp: Resolves final design issues with UDP write buffering. 100% code complete but also 100% untested.

This commit is contained in:
Gregory Nutt 2018-01-22 19:27:05 -06:00
parent fa3ad46897
commit 2d4fa19a54

View File

@ -105,9 +105,6 @@
* Private Function Prototypes * Private Function Prototypes
****************************************************************************/ ****************************************************************************/
static inline void sendto_netdev_down(FAR struct socket *psock,
FAR struct udp_conn_s *conn);
#ifdef NEED_IPDOMAIN_SUPPORT #ifdef NEED_IPDOMAIN_SUPPORT
static inline void sendto_ipselect(FAR struct net_driver_s *dev, static inline void sendto_ipselect(FAR struct net_driver_s *dev,
FAR struct udp_conn_s *conn); FAR struct udp_conn_s *conn);
@ -135,47 +132,64 @@ static inline void sendto_txnotify(FAR struct socket *psock,
****************************************************************************/ ****************************************************************************/
/**************************************************************************** /****************************************************************************
* Name: sendto_netdev_down * Name: sendto_writebuffer_release
* *
* Description: * Description:
* The network device is down. Free all write buffers. * Release the write buffer at the head of the write buffer queue.
* REVISIT: Should only free write buffers associated with the device
* that went down.
* *
* Parameters: * Parameters:
* psock The socket structure * dev - The structure of the network driver that caused the event
* conn The connection structure associated with the socket * psock - Socket state structure
* *
* Returned Value: * Returned Value:
* None * None
* *
* Assumptions:
* The network is locked
*
****************************************************************************/ ****************************************************************************/
static inline void sendto_netdev_down(FAR struct socket *psock, static void sendto_writebuffer_release(FAR struct socket *psock,
FAR struct udp_conn_s *conn) FAR struct udp_conn_s *conn)
{ {
FAR sq_entry_t *entry; FAR struct udp_wrbuffer_s *wrb;
FAR sq_entry_t *next; int ret = OK;
/* Do not allow any further callbacks */ do
if (psock->s_sndcb != NULL)
{ {
psock->s_sndcb->flags = 0; /* Check if the write queue became empty */
psock->s_sndcb->event = NULL;
if (sq_empty(&conn->write_q))
{
/* Yes.. stifle any further callbacks until more write data is
* enqueued.
*/
psock->s_sndcb->flags = 0;
psock->s_sndcb->priv = NULL;
psock->s_sndcb->event = NULL;
wrb = NULL;
}
else
{
/* Remove the write buffer from the head of the write buffer queue
* and release it.
*/
wrb = (FAR struct udp_wrbuffer_s *)sq_remfirst(&conn->write_q);
DEBUGASSERT(wrb != NULL);
udp_wrbuffer_release(wrb);
/* Set up for the next packet transfer by setting the connection
* address to the address of the next packet now at the header of
* the write buffer queue.
*/
ret = sendto_next_transfer(psock, conn);
}
} }
while (wrb != NULL && ret < 0);
/* Free all queued write buffers */
for (entry = sq_peek(&conn->write_q); entry; entry = next)
{
next = sq_next(entry);
udp_wrbuffer_release((FAR struct udp_wrbuffer_s *)entry);
}
/* Reset write buffering variables */
sq_init(&conn->write_q);
} }
/**************************************************************************** /****************************************************************************
@ -254,7 +268,7 @@ static inline void sendto_ipselect(FAR struct net_driver_s *dev,
#ifdef CONFIG_NET_ETHERNET #ifdef CONFIG_NET_ETHERNET
static inline bool sendto_addrcheck(FAR struct udp_conn_s *conn, static inline bool sendto_addrcheck(FAR struct udp_conn_s *conn,
FAR struct net_driver_s *dev) FAR struct net_driver_s *dev)
{ {
/* REVISIT: Could the MAC address not also be in a routing table? */ /* REVISIT: Could the MAC address not also be in a routing table? */
@ -458,7 +472,6 @@ static uint16_t sendto_eventhandler(FAR struct net_driver_s *dev,
{ {
FAR struct udp_conn_s *conn = (FAR struct udp_conn_s *)pvconn; FAR struct udp_conn_s *conn = (FAR struct udp_conn_s *)pvconn;
FAR struct socket *psock = (FAR struct socket *)pvpriv; FAR struct socket *psock = (FAR struct socket *)pvpriv;
int ret;
ninfo("flags: %04x\n", flags); ninfo("flags: %04x\n", flags);
@ -468,9 +481,11 @@ static uint16_t sendto_eventhandler(FAR struct net_driver_s *dev,
{ {
ninfo("Device down: %04x\n", flags); ninfo("Device down: %04x\n", flags);
/* Free write buffers and terminate polling */ /* Free the write buffer at the head of the queue and attempt to setup
* the next transfer.
*/
sendto_netdev_down(psock, conn); sendto_writebuffer_release(psock, conn);
return flags; return flags;
} }
@ -495,7 +510,6 @@ static uint16_t sendto_eventhandler(FAR struct net_driver_s *dev,
if (sendto_addrcheck(conn, dev)) if (sendto_addrcheck(conn, dev))
{ {
FAR struct udp_wrbuffer_s *wrb; FAR struct udp_wrbuffer_s *wrb;
FAR struct udp_wrbuffer_s *tmp;
size_t sndlen; size_t sndlen;
/* Peek at the head of the write queue (but don't remove anything /* Peek at the head of the write queue (but don't remove anything
@ -530,41 +544,11 @@ static uint16_t sendto_eventhandler(FAR struct net_driver_s *dev,
devif_iob_send(dev, wrb->wb_iob, sndlen, 0); devif_iob_send(dev, wrb->wb_iob, sndlen, 0);
/* Remove and free the write buffer from the head of the write /* Free the write buffer at the head of the queue and attempt to
* buffer queue. * setup the next transfer.
*/ */
do sendto_writebuffer_release(psock, conn);
{
tmp = (FAR struct udp_wrbuffer_s *)sq_remfirst(&conn->write_q);
DEBUGASSERT(tmp == wrb);
udp_wrbuffer_release(tmp);
/* Check if the write queue became empty */
if (sq_empty(&conn->write_q))
{
/* Yes.. stifle any further callbacks until more write
* data is enqueued.
*/
psock->s_sndcb->flags = 0;
psock->s_sndcb->priv = NULL;
psock->s_sndcb->event = NULL;
ret = OK;
}
else
{
/* Set up for the next packet transfer by setting the
* connection address to the address of the next packet
* now at the header of of the write buffer queue.
*/
ret = sendto_next_transfer(psock, conn);
}
}
while (ret < 0);
/* Only one data can be sent by low level driver at once, /* Only one data can be sent by low level driver at once,
* tell the caller stop polling the other connections. * tell the caller stop polling the other connections.
@ -579,28 +563,11 @@ static uint16_t sendto_eventhandler(FAR struct net_driver_s *dev,
else if (sendto_timeout(psock, conn)) else if (sendto_timeout(psock, conn))
{ {
FAR struct udp_wrbuffer_s *wrb; /* Free the write buffer at the head of the queue and attempt to setup
* the next transfer.
*/
do sendto_writebuffer_release(psock, conn);
{
/* Remove and free the write buffer from the head of the write
* buffer queue. Here we know that the write queue is not empty
* because the entry at the head of the queue just timed out!
*/
wrb = (FAR struct udp_wrbuffer_s *)sq_remfirst(&conn->write_q);
DEBUGASSERT(wrb != NULL);
udp_wrbuffer_release(wrb);
/* Set up for the next packet transfer by setting the connection
* address to the address of the next packet now at the header of the
* write buffer queue.
*/
ret = sendto_next_transfer(psock, conn);
}
while (ret < 0);
} }
#endif /* CONFIG_NET_SOCKOPTS */ #endif /* CONFIG_NET_SOCKOPTS */
@ -799,9 +766,8 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf,
ret = sendto_next_transfer(psock, conn); ret = sendto_next_transfer(psock, conn);
if (ret < 0) if (ret < 0)
{ {
/* REVISIT: An error here is not recoverable */ (void)sq_remlast(&conn->write_q);
goto errout_with_wrb;
goto errout_with_lock;
} }
/* Notify the device driver of the availability of TX data */ /* Notify the device driver of the availability of TX data */