TCP: add writable check during poll

When a poll requesting POLLOUT happens, the poll should return
immediately if a write will not block.  This change adds that, as
opposed to the old behaviour of blocking until a timer from the
Ethernet driver eventually triggers the poll to complete.

This is only implemented for buffered TCP.  Unbuffered TCP should
behave as before.
This commit is contained in:
Andrew Webster 2016-01-22 15:52:14 -06:00 committed by Gregory Nutt
parent 99f5fcda70
commit df211ee46a
5 changed files with 144 additions and 3 deletions

View File

@ -1,7 +1,7 @@
/****************************************************************************
* net/tcp/tcp.h
*
* Copyright (C) 2014-2015 Gregory Nutt. All rights reserved.
* Copyright (C) 2014-2016 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -1143,6 +1143,35 @@ struct socket;
ssize_t psock_tcp_send(FAR struct socket *psock, FAR const void *buf,
size_t len);
/****************************************************************************
* Function: psock_tcp_cansend
*
* Description:
* psock_tcp_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.
*
* Parameters:
* psock An instance of the internal socket structure.
*
* Returned Value:
* OK
* At least one byte of data could be succesfully written.
* -EWOULDBLOCK
* There is no room in the output buffer.
* -EBADF
* An invalid descriptor was specified.
* -ENOTCONN
* The socket is not connected.
*
* Assumptions:
* Not running at the interrupt level
*
****************************************************************************/
int psock_tcp_cansend(FAR struct socket *psock);
/****************************************************************************
* Function: tcp_wrbuffer_initialize
*
@ -1197,6 +1226,21 @@ FAR struct tcp_wrbuffer_s *tcp_wrbuffer_alloc(void);
void tcp_wrbuffer_release(FAR struct tcp_wrbuffer_s *wrb);
#endif /* CONFIG_NET_TCP_WRITE_BUFFERS */
/****************************************************************************
* Function: tcp_wrbuffer_test
*
* Description:
* Check if there is room in the write buffer. Does not reserve any space.
*
* Assumptions:
* None.
*
****************************************************************************/
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
int tcp_wrbuffer_test(void);
#endif /* CONFIG_NET_TCP_WRITE_BUFFERS */
/****************************************************************************
* Function: tcp_wrbuffer_dump
*

View File

@ -1,7 +1,7 @@
/****************************************************************************
* net/tcp/tcp_netpoll.c
*
* Copyright (C) 2008-2009, 2011-2015 Gregory Nutt. All rights reserved.
* Copyright (C) 2008-2009, 2011-2016 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -282,6 +282,10 @@ int tcp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds)
fds->revents |= (POLLERR | POLLHUP);
}
else if (_SS_ISCONNECTED(psock->s_flags) && psock_tcp_cansend(psock) >= 0)
{
fds->revents |= (POLLWRNORM & fds->events);
}
/* Check if any requested events are already in effect */

View File

@ -1,7 +1,7 @@
/****************************************************************************
* net/tcp/tcp_send_buffered.c
*
* Copyright (C) 2007-2014 Gregory Nutt. All rights reserved.
* Copyright (C) 2007-2014, 2016 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
* Jason Jiang <jasonj@live.cn>
*
@ -1104,4 +1104,53 @@ errout:
return ERROR;
}
/****************************************************************************
* Function: psock_tcp_cansend
*
* Description:
* psock_tcp_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.
*
* Parameters:
* psock An instance of the internal socket structure.
*
* Returned Value:
* OK
* At least one byte of data could be succesfully written.
* -EWOULDBLOCK
* There is no room in the output buffer.
* -EBADF
* An invalid descriptor was specified.
* -ENOTCONN
* The socket is not connected.
*
* Assumptions:
* Not running at the interrupt level
*
****************************************************************************/
int psock_tcp_cansend(FAR struct socket *psock)
{
if (!psock || psock->s_crefs <= 0)
{
ndbg("ERROR: Invalid socket\n");
return -EBADF;
}
if (psock->s_type != SOCK_STREAM || !_SS_ISCONNECTED(psock->s_flags))
{
ndbg("ERROR: Not connected\n");
return -ENOTCONN;
}
if (tcp_wrbuffer_test())
{
return -EWOULDBLOCK;
}
return OK;
}
#endif /* CONFIG_NET && CONFIG_NET_TCP && CONFIG_NET_TCP_WRITE_BUFFERS */

View File

@ -880,4 +880,30 @@ errout:
return ERROR;
}
/****************************************************************************
* Function: psock_tcp_cansend
*
* Description:
* psock_tcp_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.
*
* Parameters:
* psock An instance of the internal socket structure.
*
* Returned Value:
* OK (Function not implemented).
*
* Assumptions:
* None
*
****************************************************************************/
int psock_tcp_cansend(FAR struct socket *psock)
{
/* TODO: return OK unless someone is waiting for a packet to send */
return OK;
}
#endif /* CONFIG_NET && CONFIG_NET_TCP && !CONFIG_NET_TCP_WRITE_BUFFERS */

View File

@ -201,4 +201,22 @@ void tcp_wrbuffer_release(FAR struct tcp_wrbuffer_s *wrb)
sem_post(&g_wrbuffer.sem);
}
/****************************************************************************
* Function: tcp_wrbuffer_test
*
* Description:
* Check if there is room in the write buffer. Does not reserve any space.
*
* Assumptions:
* None.
*
****************************************************************************/
int tcp_wrbuffer_test(void)
{
int val = 0;
sem_getvalue(&g_wrbuffer.sem, &val);
return val > 0 ? OK : ERROR;
}
#endif /* CONFIG_NET && CONFIG_NET_TCP && CONFIG_NET_TCP_WRITE_BUFFERS */