diff --git a/Documentation/reference/os/iob.rst b/Documentation/reference/os/iob.rst index 8996ca159b3..908dce8ba4e 100644 --- a/Documentation/reference/os/iob.rst +++ b/Documentation/reference/os/iob.rst @@ -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. diff --git a/fs/procfs/fs_procfsiobinfo.c b/fs/procfs/fs_procfsiobinfo.c index dad8fdc4a4d..4e9f2e4cf4d 100644 --- a/fs/procfs/fs_procfsiobinfo.c +++ b/fs/procfs/fs_procfsiobinfo.c @@ -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", diff --git a/include/nuttx/mm/iob.h b/include/nuttx/mm/iob.h index 31bbf06cfc1..ece3b8ca68d 100644 --- a/include/nuttx/mm/iob.h +++ b/include/nuttx/mm/iob.h @@ -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 */ /**************************************************************************** diff --git a/mm/iob/Make.defs b/mm/iob/Make.defs index 1255319f839..4aafe3a5481 100644 --- a/mm/iob/Make.defs +++ b/mm/iob/Make.defs @@ -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 diff --git a/mm/iob/iob_add_queue.c b/mm/iob/iob_add_queue.c index d2c40cc6b7a..04f26802f18 100644 --- a/mm/iob/iob_add_queue.c +++ b/mm/iob/iob_add_queue.c @@ -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 */ diff --git a/mm/iob/iob_alloc_qentry.c b/mm/iob/iob_alloc_qentry.c index 588a6d29b0f..dd33b638bd8 100644 --- a/mm/iob/iob_alloc_qentry.c +++ b/mm/iob/iob_alloc_qentry.c @@ -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); diff --git a/net/can/can_callback.c b/net/can/can_callback.c index f8f0fae1d22..4ec5f71467d 100644 --- a/net/can/can_callback.c +++ b/net/can/can_callback.c @@ -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); diff --git a/net/icmp/icmp_input.c b/net/icmp/icmp_input.c index a184bdfe823..4eef3710f07 100644 --- a/net/icmp/icmp_input.c +++ b/net/icmp/icmp_input.c @@ -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); diff --git a/net/icmpv6/icmpv6_input.c b/net/icmpv6/icmpv6_input.c index a6d4cb04968..d6a72ebb932 100644 --- a/net/icmpv6/icmpv6_input.c +++ b/net/icmpv6/icmpv6_input.c @@ -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); diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h index 9f3e19347a1..978606aa273 100644 --- a/net/tcp/tcp.h +++ b/net/tcp/tcp.h @@ -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 diff --git a/net/tcp/tcp_callback.c b/net/tcp/tcp_callback.c index 2d849a289af..e40c480e6d7 100644 --- a/net/tcp/tcp_callback.c +++ b/net/tcp/tcp_callback.c @@ -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; } diff --git a/net/tcp/tcp_conn.c b/net/tcp/tcp_conn.c index 8d6c806815a..236c674df52 100644 --- a/net/tcp/tcp_conn.c +++ b/net/tcp/tcp_conn.c @@ -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 */ diff --git a/net/tcp/tcp_input.c b/net/tcp/tcp_input.c index 99e59364af7..b848baa82f1 100644 --- a/net/tcp/tcp_input.c +++ b/net/tcp/tcp_input.c @@ -59,6 +59,8 @@ #include #include +#include + #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 diff --git a/net/tcp/tcp_recvfrom.c b/net/tcp/tcp_recvfrom.c index b156241c8ef..3fac94b4874 100644 --- a/net/tcp/tcp_recvfrom.c +++ b/net/tcp/tcp_recvfrom.c @@ -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 diff --git a/net/udp/udp_callback.c b/net/udp/udp_callback.c index 6001b055dd4..fb7f074ccf2 100644 --- a/net/udp/udp_callback.c +++ b/net/udp/udp_callback.c @@ -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);