net/tcp: cache the drop packet to avoid retransmit

Change-Id: Ia27ed17a7708df8481ac96d9a3a870b6ccef4d24
Signed-off-by: chao.an <anchao@xiaomi.com>
This commit is contained in:
chao.an
2021-02-27 11:08:36 +08:00
committed by chao an
parent 608f380aee
commit 800421e231
15 changed files with 261 additions and 30 deletions
+2 -2
View File
@@ -203,12 +203,12 @@ Public Function Prototypes
Free an entire buffer chain, starting at the
beginning of the I/O buffer chain
.. c:function:: int iob_add_queue(FAR struct iob_s *iob, FAR struct iob_queue_s *iobq)
.. c:function:: int iob_add_queue(FAR struct iob_s *iob, FAR void *priv, FAR struct iob_queue_s *iobq)
Add one I/O buffer chain to the end of a queue.
May fail due to lack of resources.
.. c:function:: void iob_tryadd_queue(FAR struct iob_s *iob, FAR struct iob_queue_s *iobq)
.. c:function:: void iob_tryadd_queue(FAR struct iob_s *iob, FAR void *priv, FAR struct iob_queue_s *iobq)
Add one I/O buffer chain to the end of a queue
without waiting for resources to become free.
+1
View File
@@ -144,6 +144,7 @@ static FAR const char *g_iob_user_names[] =
#endif
#if defined(CONFIG_NET_TCP) && !defined(NET_TCP_NO_STACK)
"tcp_readahead",
"tcp_pendingahead",
#endif
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
"tcp_writebuffer",
+9 -2
View File
@@ -140,6 +140,10 @@ struct iob_qentry_s
/* Payload -- Head of the I/O buffer chain */
FAR struct iob_s *qe_head;
/* Private data */
FAR void *qe_priv;
};
/* The I/O buffer queue head structure */
@@ -196,6 +200,7 @@ enum iob_user_e
#endif
#if defined(CONFIG_NET_TCP) && !defined(NET_TCP_NO_STACK)
IOBUSER_NET_TCP_READAHEAD,
IOBUSER_NET_TCP_PENDINGAHEAD,
#endif
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
IOBUSER_NET_TCP_WRITEBUFFER,
@@ -377,7 +382,8 @@ void iob_free_chain(FAR struct iob_s *iob, enum iob_user_e producerid);
****************************************************************************/
#if CONFIG_IOB_NCHAINS > 0
int iob_add_queue(FAR struct iob_s *iob, FAR struct iob_queue_s *iobq);
int iob_add_queue(FAR struct iob_s *iob, FAR void *priv,
FAR struct iob_queue_s *iobq);
#endif /* CONFIG_IOB_NCHAINS > 0 */
/****************************************************************************
@@ -390,7 +396,8 @@ int iob_add_queue(FAR struct iob_s *iob, FAR struct iob_queue_s *iobq);
****************************************************************************/
#if CONFIG_IOB_NCHAINS > 0
int iob_tryadd_queue(FAR struct iob_s *iob, FAR struct iob_queue_s *iobq);
int iob_tryadd_queue(FAR struct iob_s *iob, FAR void *priv,
FAR struct iob_queue_s *iobq);
#endif /* CONFIG_IOB_NCHAINS > 0 */
/****************************************************************************
+1 -1
View File
@@ -40,7 +40,7 @@ ifeq ($(CONFIG_MM_IOB),y)
CSRCS += iob_add_queue.c iob_alloc.c iob_alloc_qentry.c iob_clone.c
CSRCS += iob_concat.c iob_copyin.c iob_copyout.c iob_contig.c iob_free.c
CSRCS += iob_free_chain.c iob_free_qentry.c iob_destroy_queue.c
CSRCS += iob_get_queue_count.c
CSRCS += iob_free_queue.c iob_get_queue_count.c
CSRCS += iob_initialize.c iob_pack.c iob_peek_queue.c iob_remove_queue.c
CSRCS += iob_statistics.c iob_trimhead.c iob_trimhead_queue.c iob_trimtail.c
CSRCS += iob_navail.c
+9 -5
View File
@@ -72,11 +72,13 @@
static int iob_add_queue_internal(FAR struct iob_s *iob,
FAR struct iob_queue_s *iobq,
FAR struct iob_qentry_s *qentry)
FAR struct iob_qentry_s *qentry,
FAR void *priv)
{
/* Add the I/O buffer chain to the container */
qentry->qe_head = iob;
qentry->qe_priv = priv;
/* Add the container to the end of the queue */
@@ -109,7 +111,8 @@ static int iob_add_queue_internal(FAR struct iob_s *iob,
*
****************************************************************************/
int iob_add_queue(FAR struct iob_s *iob, FAR struct iob_queue_s *iobq)
int iob_add_queue(FAR struct iob_s *iob, FAR void *priv,
FAR struct iob_queue_s *iobq)
{
FAR struct iob_qentry_s *qentry;
@@ -122,7 +125,7 @@ int iob_add_queue(FAR struct iob_s *iob, FAR struct iob_queue_s *iobq)
return -ENOMEM;
}
return iob_add_queue_internal(iob, iobq, qentry);
return iob_add_queue_internal(iob, iobq, qentry, priv);
}
/****************************************************************************
@@ -134,7 +137,8 @@ int iob_add_queue(FAR struct iob_s *iob, FAR struct iob_queue_s *iobq)
*
****************************************************************************/
int iob_tryadd_queue(FAR struct iob_s *iob, FAR struct iob_queue_s *iobq)
int iob_tryadd_queue(FAR struct iob_s *iob, FAR void *priv,
FAR struct iob_queue_s *iobq)
{
FAR struct iob_qentry_s *qentry;
@@ -147,6 +151,6 @@ int iob_tryadd_queue(FAR struct iob_s *iob, FAR struct iob_queue_s *iobq)
return -ENOMEM;
}
return iob_add_queue_internal(iob, iobq, qentry);
return iob_add_queue_internal(iob, iobq, qentry, priv);
}
#endif /* CONFIG_IOB_NCHAINS > 0 */
+1
View File
@@ -238,6 +238,7 @@ FAR struct iob_qentry_s *iob_tryalloc_qentry(void)
/* Put the I/O buffer in a known state */
iobq->qe_head = NULL; /* Nothing is contained */
iobq->qe_priv = NULL;
}
leave_critical_section(flags);
+1 -1
View File
@@ -221,7 +221,7 @@ uint16_t can_datahandler(FAR struct can_conn_s *conn, FAR uint8_t *buffer,
* without waiting).
*/
ret = iob_tryadd_queue(iob, &conn->readahead);
ret = iob_tryadd_queue(iob, NULL, &conn->readahead);
if (ret < 0)
{
nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret);
+1 -1
View File
@@ -189,7 +189,7 @@ static uint16_t icmp_datahandler(FAR struct net_driver_s *dev,
* without waiting).
*/
ret = iob_tryadd_queue(iob, &conn->readahead);
ret = iob_tryadd_queue(iob, NULL, &conn->readahead);
if (ret < 0)
{
nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret);
+1 -1
View File
@@ -192,7 +192,7 @@ static uint16_t icmpv6_datahandler(FAR struct net_driver_s *dev,
* without waiting).
*/
ret = iob_tryadd_queue(iob, &conn->readahead);
ret = iob_tryadd_queue(iob, NULL, &conn->readahead);
if (ret < 0)
{
nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret);
+13 -2
View File
@@ -214,7 +214,15 @@ struct tcp_conn_s
* where the TCP/IP read-ahead data is retained.
*/
struct iob_queue_s readahead; /* Read-ahead buffering */
struct iob_queue_s readahead; /* Read-ahead buffering */
/* Pending-ahead buffering.
*
* pendingahead - A singly linked list of type struct iob_qentry_s
* where the TCP/IP pending-ahead data is retained.
*/
struct iob_queue_s pendingahead; /* Pending-ahead buffering */
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
/* Write buffering
@@ -1120,6 +1128,8 @@ uint16_t tcp_callback(FAR struct net_driver_s *dev,
* buffer - A pointer to the buffer to be copied to the read-ahead
* buffers
* buflen - The number of bytes to copy to the read-ahead buffer.
* priv - Private data.
* producerid - id representing who is producing the IOB.
*
* Returned Value:
* The number of bytes actually buffered is returned. This will be either
@@ -1133,7 +1143,8 @@ uint16_t tcp_callback(FAR struct net_driver_s *dev,
****************************************************************************/
uint16_t tcp_datahandler(FAR struct tcp_conn_s *conn, FAR uint8_t *buffer,
uint16_t nbytes);
uint16_t nbytes, FAR void *priv,
enum iob_user_e producerid);
/****************************************************************************
* Name: tcp_backlogcreate
+28 -9
View File
@@ -99,7 +99,8 @@ tcp_data_event(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
* partial packets will not be buffered.
*/
recvlen = tcp_datahandler(conn, buffer, buflen);
recvlen = tcp_datahandler(conn, buffer, buflen, NULL,
IOBUSER_NET_TCP_READAHEAD);
if (recvlen < buflen)
{
/* There is no handler to receive new data and there are no free
@@ -238,27 +239,45 @@ uint16_t tcp_callback(FAR struct net_driver_s *dev,
****************************************************************************/
uint16_t tcp_datahandler(FAR struct tcp_conn_s *conn, FAR uint8_t *buffer,
uint16_t buflen)
uint16_t buflen, FAR void *priv,
enum iob_user_e producerid)
{
FAR struct iob_queue_s *queue;
FAR struct iob_s *iob;
bool throttled = true;
int ret;
/* Get the I/O buffer queue from iob producer id */
if (producerid == IOBUSER_NET_TCP_READAHEAD)
{
queue = &conn->readahead;
}
else if (producerid == IOBUSER_NET_TCP_PENDINGAHEAD)
{
queue = &conn->pendingahead;
}
else
{
nwarn("ERROR: Invalid iob produce id\n");
return 0;
}
/* Try to allocate on I/O buffer to start the chain without waiting (and
* throttling as necessary). If we would have to wait, then drop the
* packet.
*/
iob = iob_tryalloc(throttled, IOBUSER_NET_TCP_READAHEAD);
iob = iob_tryalloc(throttled, producerid);
if (iob == NULL)
{
#if CONFIG_IOB_THROTTLE > 0
if (IOB_QEMPTY(&conn->readahead))
if (IOB_QEMPTY(queue))
{
/* Fallback out of the throttled entry */
throttled = false;
iob = iob_tryalloc(throttled, IOBUSER_NET_TCP_READAHEAD);
iob = iob_tryalloc(throttled, producerid);
}
#endif
@@ -272,7 +291,7 @@ uint16_t tcp_datahandler(FAR struct tcp_conn_s *conn, FAR uint8_t *buffer,
/* Copy the new appdata into the I/O buffer chain (without waiting) */
ret = iob_trycopyin(iob, buffer, buflen, 0, throttled,
IOBUSER_NET_TCP_READAHEAD);
producerid);
if (ret < 0)
{
/* On a failure, iob_copyin return a negated error value but does
@@ -280,7 +299,7 @@ uint16_t tcp_datahandler(FAR struct tcp_conn_s *conn, FAR uint8_t *buffer,
*/
nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret);
iob_free_chain(iob, IOBUSER_NET_TCP_READAHEAD);
iob_free_chain(iob, producerid);
return 0;
}
@@ -288,11 +307,11 @@ uint16_t tcp_datahandler(FAR struct tcp_conn_s *conn, FAR uint8_t *buffer,
* without waiting).
*/
ret = iob_tryadd_queue(iob, &conn->readahead);
ret = iob_tryadd_queue(iob, priv, queue);
if (ret < 0)
{
nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret);
iob_free_chain(iob, IOBUSER_NET_TCP_READAHEAD);
iob_free_chain(iob, producerid);
return 0;
}
+12
View File
@@ -789,6 +789,10 @@ void tcp_free(FAR struct tcp_conn_s *conn)
iob_destroy_queue(&conn->readahead, IOBUSER_NET_TCP_READAHEAD);
/* Release any pending-ahead buffers attached to the connection */
iob_destroy_queue(&conn->pendingahead, IOBUSER_NET_TCP_PENDINGAHEAD);
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
/* Release any write buffers attached to the connection */
@@ -1032,6 +1036,10 @@ FAR struct tcp_conn_s *tcp_alloc_accept(FAR struct net_driver_s *dev,
IOB_QINIT(&conn->readahead);
/* Initialize the list of TCP pending-ahead buffers */
IOB_QINIT(&conn->pendingahead);
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
/* Initialize the write buffer lists */
@@ -1299,6 +1307,10 @@ int tcp_connect(FAR struct tcp_conn_s *conn, FAR const struct sockaddr *addr)
IOB_QINIT(&conn->readahead);
/* Initialize the list of TCP pending-ahead buffers */
IOB_QINIT(&conn->pendingahead);
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
/* Initialize the TCP write buffer lists */
+178 -3
View File
@@ -59,6 +59,8 @@
#include <nuttx/net/ip.h>
#include <nuttx/net/tcp.h>
#include <nuttx/kmalloc.h>
#include "devif/devif.h"
#include "utils/utils.h"
#include "tcp/tcp.h"
@@ -73,6 +75,58 @@
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: tcp_input_cache
****************************************************************************/
static void tcp_input_cache(FAR struct net_driver_s *dev,
FAR struct tcp_conn_s *conn, unsigned int iplen)
{
uint8_t header[iplen + NET_LL_HDRLEN(dev) + sizeof(struct tcp_hdr_s)];
FAR struct iob_qentry_s *qentry;
FAR struct tcp_hdr_s *incoming;
FAR struct tcp_hdr_s *cached;
uint32_t ackseq;
uint32_t rcvseq;
if (dev->d_len < sizeof(header))
{
return;
}
/* Get a pointer to the TCP header. The TCP header lies just after the
* the link layer header and the IP header.
*/
incoming = (FAR struct tcp_hdr_s *)&dev->d_buf[iplen + NET_LL_HDRLEN(dev)];
/* Get the sequence number of that has just been acknowledged by this
* incoming packet.
*/
ackseq = tcp_getsequence(incoming->seqno);
rcvseq = tcp_getsequence(conn->rcvseq);
if (ackseq < rcvseq)
{
return;
}
for (qentry = conn->pendingahead.qh_head;
qentry != NULL; qentry = qentry->qe_flink)
{
(void)iob_copyout(header, qentry->qe_head, sizeof(header), 0);
cached = (FAR struct tcp_hdr_s *)&header[iplen + NET_LL_HDRLEN(dev)];
rcvseq = tcp_getsequence(cached->seqno);
if (rcvseq == ackseq)
{
return;
}
}
tcp_datahandler(conn, dev->d_buf, dev->d_len,
(void *)iplen, IOBUSER_NET_TCP_PENDINGAHEAD);
}
/****************************************************************************
* Name: tcp_input
*
@@ -93,7 +147,7 @@
****************************************************************************/
static void tcp_input(FAR struct net_driver_s *dev, uint8_t domain,
unsigned int iplen)
unsigned int iplen, FAR struct tcp_conn_s **active)
{
FAR struct tcp_hdr_s *tcp;
FAR struct tcp_conn_s *conn = NULL;
@@ -151,6 +205,11 @@ static void tcp_input(FAR struct net_driver_s *dev, uint8_t domain,
conn = tcp_active(dev, tcp);
if (conn)
{
if (active)
{
*active = conn;
}
/* We found an active connection.. Check for the subsequent SYN
* arriving in TCP_SYN_RCVD state after the SYNACK packet was
* lost. To avoid other issues, reset any active connection
@@ -200,6 +259,11 @@ static void tcp_input(FAR struct net_driver_s *dev, uint8_t domain,
conn = tcp_alloc_accept(dev, tcp);
if (conn)
{
if (active)
{
*active = conn;
}
/* The connection structure was successfully allocated and has
* been initialized in the TCP_SYN_RECVD state. The expected
* sequence of events is then the rest of the 3-way handshake:
@@ -453,6 +517,11 @@ found:
if ((dev->d_len > 0 || ((tcp->flags & (TCP_SYN | TCP_FIN)) != 0)) &&
memcmp(tcp->seqno, conn->rcvseq, 4) != 0)
{
/* Restore the data length */
dev->d_len += hdrlen;
tcp_input_cache(dev, conn, iplen);
tcp_send(dev, conn, TCP_ACK, tcpiplen);
return;
}
@@ -1017,6 +1086,95 @@ drop:
dev->d_len = 0;
}
/****************************************************************************
* Name: tcp_process_cache
****************************************************************************/
static void tcp_process_cache(FAR struct net_driver_s *dev, uint8_t domain,
FAR struct tcp_conn_s *conn)
{
FAR struct iob_qentry_s *qentry;
FAR uint8_t *reassemble = NULL;
FAR struct iob_qentry_s *next;
FAR struct tcp_hdr_s tcp;
FAR struct iob_s *iob;
FAR uint8_t *d_buf;
unsigned int iplen;
uint32_t ackseq;
uint32_t rcvseq;
uint16_t d_len;
if (!conn || !iob_peek_queue(&conn->pendingahead))
{
return;
}
d_len = dev->d_len;
d_buf = dev->d_buf;
for (qentry = conn->pendingahead.qh_head; qentry != NULL; qentry = next)
{
next = qentry->qe_flink;
iob = qentry->qe_head;
iplen = (intptr_t)qentry->qe_priv;
(void)iob_copyout((FAR uint8_t *)&tcp, iob, sizeof(tcp),
NET_LL_HDRLEN(dev) + iplen);
rcvseq = tcp_getsequence(conn->rcvseq);
ackseq = tcp_getsequence(tcp.seqno);
if (rcvseq == ackseq)
{
if (iob->io_pktlen > iob->io_len)
{
if (reassemble == NULL)
{
reassemble = kmm_malloc(CONFIG_NET_ETH_PKTSIZE);
if (reassemble == NULL)
{
iob_destroy_queue(&conn->pendingahead,
IOBUSER_NET_TCP_PENDINGAHEAD);
break;
}
}
(void)iob_copyout(reassemble, iob, iob->io_pktlen, 0);
dev->d_buf = reassemble;
}
else
{
dev->d_buf = IOB_DATA(iob);
}
dev->d_len = iob->io_pktlen - NET_LL_HDRLEN(dev);
tcp_input(dev, domain, iplen, NULL);
iob_free_queue(iob, &conn->pendingahead,
IOBUSER_NET_TCP_PENDINGAHEAD);
/* Re-traverse the pending list */
qentry = conn->pendingahead.qh_head;
}
else if (ackseq < rcvseq)
{
iob_free_queue(iob, &conn->pendingahead,
IOBUSER_NET_TCP_PENDINGAHEAD);
}
}
dev->d_len = d_len;
dev->d_buf = d_buf;
if (reassemble)
{
kmm_free(reassemble);
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
@@ -1042,6 +1200,7 @@ drop:
void tcp_ipv4_input(FAR struct net_driver_s *dev)
{
FAR struct ipv4_hdr_s *ipv4 = IPv4BUF;
FAR struct tcp_conn_s *conn = NULL;
uint16_t iphdrlen;
/* Configure to receive an TCP IPv4 packet */
@@ -1054,7 +1213,14 @@ void tcp_ipv4_input(FAR struct net_driver_s *dev)
/* Then process in the TCP IPv4 input */
tcp_input(dev, PF_INET, iphdrlen);
tcp_input(dev, PF_INET, iphdrlen, &conn);
/* Try the pending cache here */
if (conn)
{
tcp_process_cache(dev, PF_INET, conn);
}
}
#endif
@@ -1081,13 +1247,22 @@ void tcp_ipv4_input(FAR struct net_driver_s *dev)
#ifdef CONFIG_NET_IPv6
void tcp_ipv6_input(FAR struct net_driver_s *dev, unsigned int iplen)
{
FAR struct tcp_conn_s *conn = NULL;
/* Configure to receive an TCP IPv6 packet */
tcp_ipv6_select(dev);
/* Then process in the TCP IPv6 input */
tcp_input(dev, PF_INET6, iplen);
tcp_input(dev, PF_INET6, iplen, &conn);
/* Try the pending cache here */
if (conn)
{
tcp_process_cache(dev, PF_INET6, conn);
}
}
#endif
+3 -2
View File
@@ -186,9 +186,10 @@ static inline void tcp_newdata(FAR struct net_driver_s *dev,
#ifdef CONFIG_DEBUG_NET
uint16_t nsaved;
nsaved = tcp_datahandler(conn, buffer, buflen);
nsaved = tcp_datahandler(conn, buffer, buflen, NULL,
IOBUSER_NET_TCP_READAHEAD);
#else
tcp_datahandler(conn, buffer, buflen);
tcp_datahandler(conn, buffer, buflen, NULL, IOBUSER_NET_TCP_READAHEAD);
#endif
/* There are complicated buffering issues that are not addressed fully
+1 -1
View File
@@ -226,7 +226,7 @@ static uint16_t udp_datahandler(FAR struct net_driver_s *dev,
/* Add the new I/O buffer chain to the tail of the read-ahead queue */
ret = iob_tryadd_queue(iob, &conn->readahead);
ret = iob_tryadd_queue(iob, NULL, &conn->readahead);
if (ret < 0)
{
nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret);