net/tcp: wave hands on background

The time consuming of tcp waving hands(close(2)) will be affected
by network jitter, especially the wireless device cannot receive
the last-ack under worst environment, in this change we move the
tcp close callback into background and invoke the resource free
from workqueue, which will avoid the user application from being
blocked for a long time and unable to return in the call of close

Signed-off-by: chao.an <anchao@xiaomi.com>
This commit is contained in:
chao.an 2022-06-09 17:23:26 +08:00 committed by Petro Karashchenko
parent 6bd36d64e3
commit 0636c17a63
2 changed files with 46 additions and 89 deletions

View File

@ -304,6 +304,11 @@ struct tcp_conn_s
FAR struct devif_callback_s *connevents;
FAR struct devif_callback_s *connevents_tail;
/* Reference to TCP close callback instance */
FAR struct devif_callback_s *clscb;
struct work_s clswork;
#if defined(CONFIG_NET_TCP_WRITE_BUFFERS)
/* Callback instance for TCP send() */

View File

@ -39,22 +39,30 @@
#include "tcp/tcp.h"
#include "socket/socket.h"
/****************************************************************************
* Private Types
****************************************************************************/
struct tcp_close_s
{
FAR struct tcp_conn_s *cl_conn; /* Needed to handle loss of connection */
FAR struct devif_callback_s *cl_cb; /* Reference to TCP callback instance */
sem_t cl_sem; /* Signals disconnect completion */
int cl_result; /* The result of the close */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_close_work
****************************************************************************/
static void tcp_close_work(FAR void *param)
{
FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)param;
net_lock();
tcp_callback_free(conn, conn->clscb);
/* Stop the network monitor for all sockets */
tcp_stop_monitor(conn, TCP_CLOSE);
tcp_free(conn);
net_unlock();
}
/****************************************************************************
* Name: tcp_close_eventhandler
****************************************************************************/
@ -63,10 +71,7 @@ static uint16_t tcp_close_eventhandler(FAR struct net_driver_s *dev,
FAR void *pvconn, FAR void *pvpriv,
uint16_t flags)
{
FAR struct tcp_close_s *pstate = (FAR struct tcp_close_s *)pvpriv;
FAR struct tcp_conn_s *conn = pstate->cl_conn;
DEBUGASSERT(pstate != NULL);
FAR struct tcp_conn_s *conn = pvpriv;
ninfo("flags: %04x\n", flags);
@ -106,15 +111,6 @@ static uint16_t tcp_close_eventhandler(FAR struct net_driver_s *dev,
* NETWORK_DOWN event is processed further.
*/
if ((flags & NETDEV_DOWN) != 0)
{
pstate->cl_result = -ENODEV;
}
else
{
pstate->cl_result = OK;
}
goto end_wait;
}
@ -182,12 +178,14 @@ static uint16_t tcp_close_eventhandler(FAR struct net_driver_s *dev,
return flags;
end_wait:
pstate->cl_cb->flags = 0;
pstate->cl_cb->priv = NULL;
pstate->cl_cb->event = NULL;
nxsem_post(&pstate->cl_sem);
conn->clscb->flags = 0;
conn->clscb->priv = NULL;
conn->clscb->event = NULL;
/* Free network resources */
work_queue(LPWORK, &conn->clswork, tcp_close_work, conn, 0);
ninfo("Resuming\n");
return flags;
}
@ -257,7 +255,6 @@ static inline void tcp_close_txnotify(FAR struct socket *psock,
static inline int tcp_close_disconnect(FAR struct socket *psock)
{
struct tcp_close_s state;
FAR struct tcp_conn_s *conn;
int ret = OK;
@ -307,58 +304,28 @@ static inline int tcp_close_disconnect(FAR struct socket *psock)
if ((conn->tcpstateflags == TCP_ESTABLISHED ||
conn->tcpstateflags == TCP_LAST_ACK) &&
(state.cl_cb = tcp_callback_alloc(conn)) != NULL)
(conn->clscb = tcp_callback_alloc(conn)) != NULL)
{
/* Set up to receive TCP data event callbacks */
state.cl_cb->flags = (TCP_NEWDATA | TCP_POLL | TCP_DISCONN_EVENTS);
state.cl_cb->event = tcp_close_eventhandler;
/* A non-NULL value of the priv field means that lingering is
* enabled.
*/
state.cl_cb->priv = (FAR void *)&state;
/* Set up for the lingering wait */
state.cl_conn = conn;
state.cl_result = -EBUSY;
/* This semaphore is used for signaling and, hence, should not have
* priority inheritance enabled.
*/
nxsem_init(&state.cl_sem, 0, 0);
nxsem_set_protocol(&state.cl_sem, SEM_PRIO_NONE);
conn->clscb->flags = (TCP_NEWDATA | TCP_POLL | TCP_DISCONN_EVENTS);
conn->clscb->event = tcp_close_eventhandler;
conn->clscb->priv = conn;
/* Notify the device driver of the availability of TX data */
tcp_close_txnotify(psock, conn);
/* Wait for the disconnect event */
net_lockedwait(&state.cl_sem);
/* We are now disconnected */
nxsem_destroy(&state.cl_sem);
tcp_callback_free(conn, state.cl_cb);
/* Free the connection
* No more references on the connection
*/
conn->crefs = 0;
/* Get the result of the close */
ret = state.cl_result;
}
else
{
/* Stop the network monitor for all sockets */
/* Free network resources */
tcp_stop_monitor(conn, TCP_CLOSE);
tcp_free(conn);
/* Free network resources */
tcp_free(conn);
}
net_unlock();
return ret;
@ -385,7 +352,6 @@ static inline int tcp_close_disconnect(FAR struct socket *psock)
int tcp_close(FAR struct socket *psock)
{
FAR struct tcp_conn_s *conn = psock->s_conn;
int ret;
/* Perform the disconnection now */
@ -394,21 +360,7 @@ int tcp_close(FAR struct socket *psock)
/* Break any current connections and close the socket */
ret = tcp_close_disconnect(psock);
if (ret < 0)
{
/* This would normally occur only if there is a timeout
* from a lingering close.
*/
nerr("ERROR: tcp_close_disconnect failed: %d\n", ret);
return ret;
}
/* Stop the network monitor for all sockets */
tcp_stop_monitor(conn, TCP_CLOSE);
return OK;
return tcp_close_disconnect(psock);
}
#endif /* CONFIG_NET_TCP */