diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2014-06-22 11:27:57 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2014-06-22 11:27:57 -0600 |
commit | 0c72d9900e00e06e9c9b1008e795766011a9c46f (patch) | |
tree | 0a7b6144118d955836693cd0d31b2d9127418de7 | |
parent | 8cf46d1f34452aca50d6aabe3d13bd12f54d5906 (diff) | |
download | nuttx-0c72d9900e00e06e9c9b1008e795766011a9c46f.tar.gz nuttx-0c72d9900e00e06e9c9b1008e795766011a9c46f.tar.bz2 nuttx-0c72d9900e00e06e9c9b1008e795766011a9c46f.zip |
First cut at conversion of write-buffering to use I/O buffer chaings (IOBs)
27 files changed, 1081 insertions, 232 deletions
diff --git a/nuttx/include/nuttx/net/iob.h b/nuttx/include/nuttx/net/iob.h index ac3977538..34a59cc00 100644 --- a/nuttx/include/nuttx/net/iob.h +++ b/nuttx/include/nuttx/net/iob.h @@ -55,10 +55,12 @@ #define IOB_DATA(p) (&(p)->io_data[(p)->io_offset]) #define IOB_FREESPACE(p) (CONFIG_IOB_BUFSIZE - (p)->io_len - (p)->io_offset) +#if CONFIG_IOB_NCHAINS > 0 /* Queue helpers */ -#define IOB_QINIT(q) do { (q)->qh_head = 0; (q)->qh_tail = 0; } while (0) -#define IOB_QEMPTY(q) ((q)->head == NULL) +# define IOB_QINIT(q) do { (q)->qh_head = 0; (q)->qh_tail = 0; } while (0) +# define IOB_QEMPTY(q) ((q)->head == NULL) +#endif /**************************************************************************** * Public Types @@ -89,6 +91,7 @@ struct iob_s uint8_t io_data[CONFIG_IOB_BUFSIZE]; }; +#if CONFIG_IOB_NCHAINS > 0 /* This container structure supports queuing of I/O buffer chains. This * structure is intended only for internal use by the IOB module. */ @@ -113,6 +116,7 @@ struct iob_queue_s FAR struct iob_qentry_s *qh_head; FAR struct iob_qentry_s *qh_tail; }; +#endif /* CONFIG_IOB_NCHAINS > 0 */ /**************************************************************************** * Global Data @@ -173,7 +177,9 @@ void iob_free_chain(FAR struct iob_s *iob); * ****************************************************************************/ +#if CONFIG_IOB_NCHAINS > 0 int iob_add_queue(FAR struct iob_s *iob, FAR struct iob_queue_s *iobq); +#endif /* CONFIG_IOB_NCHAINS > 0 */ /**************************************************************************** * Name: iob_add_queue @@ -183,7 +189,9 @@ int iob_add_queue(FAR struct iob_s *iob, FAR struct iob_queue_s *iobq); * ****************************************************************************/ +#if CONFIG_IOB_NCHAINS > 0 FAR struct iob_s *iob_remove_queue(FAR struct iob_queue_s *iobq); +#endif /* CONFIG_IOB_NCHAINS > 0 */ /**************************************************************************** * Name: iob_free_queue @@ -193,7 +201,9 @@ FAR struct iob_s *iob_remove_queue(FAR struct iob_queue_s *iobq); * ****************************************************************************/ +#if CONFIG_IOB_NCHAINS > 0 void iob_free_queue(FAR struct iob_queue_s *qhead); +#endif /* CONFIG_IOB_NCHAINS > 0 */ /**************************************************************************** * Name: iob_copyin @@ -278,11 +288,25 @@ FAR struct iob_s *iob_pack(FAR struct iob_s *iob); * Name: iob_contig * * Description: - * Ensure that there is'len' bytes of contiguous space at the beginning + * Ensure that there is 'len' bytes of contiguous space at the beginning * of the I/O buffer chain starting at 'iob'. * ****************************************************************************/ int iob_contig(FAR struct iob_s *iob, unsigned int len); +/**************************************************************************** + * Function: iob_dump + * + * Description: + * Dump the contents of a I/O buffer chain + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG +void iob_dump(FAR const char *msg, FAR struct iob_s *iob); +#else +# define tcp_writebuffer_dump(wrb) +#endif + #endif /* _INCLUDE_NUTTX_NET_IOB_H */ diff --git a/nuttx/include/nuttx/net/uip/uip-tcp.h b/nuttx/include/nuttx/net/uip/uip-tcp.h index cbb125a74..a2234c60d 100644 --- a/nuttx/include/nuttx/net/uip/uip-tcp.h +++ b/nuttx/include/nuttx/net/uip/uip-tcp.h @@ -56,6 +56,7 @@ #include <stdint.h> #include <stdbool.h> #include <nuttx/net/uip/uipopt.h> +#include <nuttx/net/uip/uip.h> /**************************************************************************** * Pre-processor Definitions @@ -123,6 +124,25 @@ # define UIP_TCP_INITIAL_MSS UIP_TCP_MSS #endif +#ifdef CONFIG_NET_TCP_WRITE_BUFFERS +/* TCP write buffer access macros */ + +# define WRB_SEQNO(wrb) ((wrb)->wb_seqno) +# define WRB_PKTLEN(wrb) ((wrb)->wb_iob->io_pktlen) +# define WRB_SENT(wrb) ((wrb)->wb_sent) +# define WRB_NRTX(wrb) ((wrb)->wb_nrtx) +# define WRB_IOB(wrb) ((wrb)->wb_iob) +# define WRB_COPYOUT(wrb,dest,n) (iob_copyout(dest,(wrb)->wb_iob,(n),0)) +# define WRB_COPYIN(wrb,src,n) (iob_copyin((wrb)->wb_iob,src,(n),0)) +# define WRB_TRIM(wrb,n) (iob_trimhead((wrb)->wb_iob,(n))) + +#ifdef CONFIG_DEBUG +# define WRB_DUMP(msg,wrb) tcp_writebuffer_dump(msg,wrb) +#else +# define WRB_DUMP(mgs,wrb) +#endif +#endif + /**************************************************************************** * Public Type Definitions ****************************************************************************/ @@ -170,22 +190,28 @@ struct uip_conn /* Read-ahead buffering. * - * readahead - A singly linked list of type struct uip_readahead_s - * where the TCP/IP read-ahead data is retained. + * readahead - A singly linked list of type struct uip_readahead_s + * where the TCP/IP read-ahead data is retained. */ #ifdef CONFIG_NET_TCP_READAHEAD sq_queue_t readahead; /* Read-ahead buffering */ #endif - /* Write buffering */ + /* Write buffering + * + * write_q - The queue of unsent I/O buffers. The head of this + * list may be partially sent. FIFO ordering. + * unacked_q - A queue of completely sent, but unacked I/O buffer + * chains. Sequence number ordering. + */ #ifdef CONFIG_NET_TCP_WRITE_BUFFERS sq_queue_t write_q; /* Write buffering for segments */ sq_queue_t unacked_q; /* Write buffering for un-ACKed segments */ - size_t expired; /* Number segments retransmitted but not yet ACKed, + uint16_t expired; /* Number segments retransmitted but not yet ACKed, * it can only be updated at UIP_ESTABLISHED state */ - size_t sent; /* The number of bytes sent */ + uint16_t sent; /* The number of bytes sent */ uint32_t isn; /* Initial sequence number */ #endif @@ -261,14 +287,15 @@ struct uip_readahead_s /* This structure supports TCP write buffering */ #ifdef CONFIG_NET_TCP_WRITE_BUFFERS +struct iob_s; /* Forward reference */ struct tcp_wrbuffer_s { sq_entry_t wb_node; /* Supports a singly linked list */ uint32_t wb_seqno; /* Sequence number of the write segment */ - uint16_t wb_nbytes; /* Number of bytes available in this buffer */ + uint16_t wb_sent; /* Number of bytes sent from the I/O buffer chain */ uint8_t wb_nrtx; /* The number of retransmissions for the last * segment sent */ - uint8_t wb_buffer[CONFIG_NET_TCP_WRITE_BUFSIZE]; + struct iob_s *wb_iob; /* Head of the I/O buffer chain */ }; #endif diff --git a/nuttx/include/nuttx/net/uip/uip.h b/nuttx/include/nuttx/net/uip/uip.h index 798b905ac..a0cf88f5c 100644 --- a/nuttx/include/nuttx/net/uip/uip.h +++ b/nuttx/include/nuttx/net/uip/uip.h @@ -391,7 +391,14 @@ extern int uip_lockedwait(sem_t *sem); * len The maximum amount of data bytes to be sent. */ -extern void uip_send(struct uip_driver_s *dev, const void *buf, int len); +extern void uip_send(FAR struct uip_driver_s *dev, FAR const void *buf, + int len); + +#ifdef CONFIG_NET_IOB +struct iob_s; +extern void uip_iobsend(FAR struct uip_driver_s *dev, FAR struct iob_s *buf, + unsigned int len, unsigned int offset); +#endif /* uIP convenience and converting functions. * diff --git a/nuttx/include/nuttx/net/uip/uipopt.h b/nuttx/include/nuttx/net/uip/uipopt.h index 5059d7e1b..fb85b4ad7 100644 --- a/nuttx/include/nuttx/net/uip/uipopt.h +++ b/nuttx/include/nuttx/net/uip/uipopt.h @@ -305,30 +305,6 @@ # undef CONFIG_NET_NTCP_READAHEAD_BUFFERS #endif -#ifdef CONFIG_NET_TCP_WRITE_BUFFERS - /* Number of TCP write buffers */ - -# ifndef CONFIG_NET_NTCP_WRITE_BUFFERS -# define CONFIG_NET_NTCP_WRITE_BUFFERS 1 -# endif - - /* The size of one TCP write buffer */ - -# ifndef CONFIG_NET_TCP_WRITE_BUFSIZE -# define CONFIG_NET_TCP_WRITE_BUFSIZE UIP_TCP_MSS -# endif - - /* The size of the write buffer should not exceed the maximum TCP MSS */ - -# if CONFIG_NET_TCP_WRITE_BUFSIZE > UIP_TCP_MSS -# error CONFIG_NET_TCP_WRITE_BUFSIZE must not exceed UIP_TCP_MSS -# endif - -#else -# undef CONFIG_NET_TCP_WRITE_BUFSIZE -# undef CONFIG_NET_NTCP_WRITE_BUFFERS -#endif - /* Delay after receive to catch a following packet. No delay should be * required if TCP/IP read-ahead buffering is enabled. */ diff --git a/nuttx/net/iob/Kconfig b/nuttx/net/iob/Kconfig index f7793afa8..3ae153dac 100644 --- a/nuttx/net/iob/Kconfig +++ b/nuttx/net/iob/Kconfig @@ -30,10 +30,16 @@ config IOB_BUFSIZE config IOB_NCHAINS int "Number of pre-allocated I/O buffer chain heads" - default 8 + default 0 ---help--- - These tiny nodes are used as "containers" to suppor queueing of + These tiny nodes are used as "containers" to support queueing of I/O buffer chains. This will limit the number of I/O transactions - that can be "in-flight" at any give time. + that can be "in-flight" at any give time. The default value of + zero disables this features. + + These generic I/O buffer chain containers are not currently used + by any logic in NuttX. That is because their other other specialized + I/O buffer chain containers that also carry a payload of usage + specific information. endif # NET_IOB diff --git a/nuttx/net/iob/Make.defs b/nuttx/net/iob/Make.defs index e522d5885..205ef72ea 100644 --- a/nuttx/net/iob/Make.defs +++ b/nuttx/net/iob/Make.defs @@ -43,6 +43,10 @@ NET_CSRCS += iob_free_chain.c iob_free_qentry.c iob_free_queue.c NET_CSRCS += iob_initialize.c iob_pack.c iob_remove_queue.c iob_trimhead.c NET_CSRCS += iob_trimtail.c +ifeq ($(CONFIG_DEBUG),y) +NET_CSRCS += iob_dump.c +endif + # Include iob build support DEPPATH += --dep-path iob diff --git a/nuttx/net/iob/iob.h b/nuttx/net/iob/iob.h index 0507fa34d..03ea6d14d 100644 --- a/nuttx/net/iob/iob.h +++ b/nuttx/net/iob/iob.h @@ -42,6 +42,8 @@ #include <nuttx/config.h> +#include <semaphore.h> + #include <nuttx/net/iob.h> /**************************************************************************** @@ -64,6 +66,11 @@ extern FAR struct iob_s *g_iob_freelist; extern FAR struct iob_qentry_s *g_iob_freeqlist; +/* Counting semaphores that tracks the number of free IOBs/qentries */ + +extern sem_t g_iob_sem; +extern sem_t g_qentry_sem; + /**************************************************************************** * Public Data ****************************************************************************/ diff --git a/nuttx/net/iob/iob_add_queue.c b/nuttx/net/iob/iob_add_queue.c index 0a2ac3d0a..8ecc556ea 100644 --- a/nuttx/net/iob/iob_add_queue.c +++ b/nuttx/net/iob/iob_add_queue.c @@ -47,6 +47,8 @@ #include "iob.h" +#if CONFIG_IOB_NCHAINS > 0 + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -102,3 +104,5 @@ int iob_add_queue(FAR struct iob_s *iob, FAR struct iob_queue_s *iobq) return 0; } + +#endif /* CONFIG_IOB_NCHAINS > 0 */ diff --git a/nuttx/net/iob/iob_alloc.c b/nuttx/net/iob/iob_alloc.c index f9810bfa3..68e3fb602 100644 --- a/nuttx/net/iob/iob_alloc.c +++ b/nuttx/net/iob/iob_alloc.c @@ -39,6 +39,9 @@ #include <nuttx/config.h> +#include <semaphore.h> +#include <assert.h> + #include <nuttx/arch.h> #include <nuttx/net/iob.h> @@ -61,18 +64,19 @@ ****************************************************************************/ /**************************************************************************** - * Public Functions + * Private Functions ****************************************************************************/ /**************************************************************************** - * Name: iob_alloc + * Name: iob_tryalloc * * Description: - * Allocate an I/O buffer by taking the buffer at the head of the free list. + * Try to allocate an I/O buffer by taking the buffer at the head of the + * free list. * ****************************************************************************/ -FAR struct iob_s *iob_alloc(void) +static FAR struct iob_s *iob_tryalloc(void) { FAR struct iob_s *iob; irqstate_t flags; @@ -85,9 +89,12 @@ FAR struct iob_s *iob_alloc(void) iob = g_iob_freelist; if (iob) { - /* Remove the I/O buffer from the free list */ + /* Remove the I/O buffer from the free list and decrement the counting + * semaphore that tracks the number of free IOBs. + */ g_iob_freelist = iob->io_flink; + DEBUGVERIFY(sem_trywait(&g_iob_sem)); irqrestore(flags); /* Put the I/O buffer in a known state */ @@ -102,3 +109,86 @@ FAR struct iob_s *iob_alloc(void) irqrestore(flags); return NULL; } + +/**************************************************************************** + * Name: iob_allocwait + * + * Description: + * Allocate an I/O buffer, waiting if necessary. This function cannot be + * called from any interrupt level logic. + * + ****************************************************************************/ + +static FAR struct iob_s *iob_allocwait(void) +{ + FAR struct iob_s *iob; + irqstate_t flags; + int ret; + + /* The following must be atomic; interrupt must be disabled so that there + * is no conflict with interrupt level I/O buffer allocations. This is + * not as bad as it sounds because interrupts will be re-enabled while + * we are waiting for I/O buffers to become free. + */ + + flags = irqsave(); + do + { + /* Try to get an I/O buffer. If successful, the semaphore count + * will be decremented atomically. + */ + + iob = iob_tryalloc(); + if (!iob) + { + /* If not successful, then the semaphore count was less than or + * equal to zero (meaning that there are no free buffers). We + * need to wait for an I/O buffer to be released when the semaphore + * count will be incremented. + */ + + ret = sem_wait(&g_iob_sem); + + /* When we wake up from wait, an I/O buffer was returned to + * the free list. However, if there are concurrent allocations + * from interrupt handling, then I suspect that there is a + * race condition. But no harm, we will just wait again in + * that case. + */ + } + } + while (ret == OK && !iob); + + irqrestore(flags); + return iob; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: iob_alloc + * + * Description: + * Allocate an I/O buffer by taking the buffer at the head of the free list. + * + ****************************************************************************/ + +FAR struct iob_s *iob_alloc(void) +{ + /* Were we called from the interrupt level? */ + + if (up_interrupt_context()) + { + /* Yes, then try to allocate an I/O buffer without waiting */ + + return iob_tryalloc(); + } + else + { + /* Then allocate an I/O buffer, waiting as necessary */ + + return iob_allocwait(); + } +} diff --git a/nuttx/net/iob/iob_alloc_qentry.c b/nuttx/net/iob/iob_alloc_qentry.c index 0c29fbec1..21314c011 100644 --- a/nuttx/net/iob/iob_alloc_qentry.c +++ b/nuttx/net/iob/iob_alloc_qentry.c @@ -39,11 +39,16 @@ #include <nuttx/config.h> +#include <semaphore.h> +#include <assert.h> + #include <nuttx/arch.h> #include <nuttx/net/iob.h> #include "iob.h" +#if CONFIG_IOB_NCHAINS > 0 + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -61,6 +66,104 @@ ****************************************************************************/ /**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: iob_tryalloc_qentry + * + * Description: + * Try to allocate an I/O buffer chain container by taking the buffer at + * the head of the free list. This function is intended only for internal + * use by the IOB module. + * + ****************************************************************************/ + +static FAR struct iob_qentry_s *iob_tryalloc_qentry(void) +{ + FAR struct iob_qentry_s *iobq; + irqstate_t flags; + + /* We don't know what context we are called from so we use extreme measures + * to protect the free list: We disable interrupts very briefly. + */ + + flags = irqsave(); + iobq = g_iob_freeqlist; + if (iobq) + { + /* Remove the I/O buffer chain container from the free list and + * decrement the counting semaphore that tracks the number of free + * containers. + */ + + g_iob_freeqlist = iobq->qe_flink; + DEBVERIFY(sem_trywait(&g_qentry_sem)); + + /* Put the I/O buffer in a known state */ + + iobq->qe_head = NULL; /* Nothing is contained */ + } + + irqrestore(flags); + return iobq; +} + +/**************************************************************************** + * Name: iob_allocwait_qentry + * + * Description: + * Allocate an I/O buffer chain container by taking the buffer at the head + * of the free list. This function is intended only for internal use by + * the IOB module. + * + ****************************************************************************/ + +static FAR struct iob_qentry_s *iob_allocwait_qentry(void) +{ + FAR struct iob_qentry_s *qentry; + irqstate_t flags; + int ret; + + /* The following must be atomic; interrupt must be disabled so that there + * is no conflict with interrupt level I/O buffer chain container + * allocations. This is not as bad as it sounds because interrupts will be + * re-enabled while we are waiting for I/O buffers to become free. + */ + + flags = irqsave(); + do + { + /* Try to get an I/O buffer chain container. If successful, the + * semaphore count will be decremented atomically. + */ + + qentry = iob_tryalloc_qentry(); + if (!qentry) + { + /* If not successful, then the semaphore count was less than or + * equal to zero (meaning that there are no free buffers). We + * need to wait for an I/O buffer chain container to be released + * when the semaphore count will be incremented. + */ + + ret = sem_wait(&g_qentry_sem); + + /* When we wake up from wait, an I/O buffer chain container was + * returned to the free list. However, if there are concurrent + * allocations from interrupt handling, then I suspect that there + * is a race condition. But no harm, we will just wait again in + * that case. + */ + } + } + while (ret == OK && !qentry); + + irqrestore(flags); + return qentry; +} + +/**************************************************************************** * Public Functions ****************************************************************************/ @@ -87,9 +190,13 @@ FAR struct iob_qentry_s *iob_alloc_qentry(void) iobq = g_iob_freeqlist; if (iobq) { - /* Remove the I/O buffer chain container from the free list */ + /* Remove the I/O buffer chain container from the free list and + * decrement the counting semaphore that tracks the number of free + * containers. + */ g_iob_freeqlist = iobq->qe_flink; + DEBVERIFY(sem_trywait(&g_qentry_sem)); /* Put the I/O buffer in a known state */ @@ -99,3 +206,5 @@ FAR struct iob_qentry_s *iob_alloc_qentry(void) irqrestore(flags); return iobq; } + +#endif /* CONFIG_IOB_NCHAINS > 0 */ diff --git a/nuttx/net/iob/iob_dump.c b/nuttx/net/iob/iob_dump.c new file mode 100644 index 000000000..a4d1cf04a --- /dev/null +++ b/nuttx/net/iob/iob_dump.c @@ -0,0 +1,177 @@ +/**************************************************************************** + * net/iob/iob_dump.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <debug.h> + +#include <nuttx/net/iob.h> + +#ifdef CONFIG_DEBUG + +/**************************************************************************** + * Pre-processor definitions + ****************************************************************************/ + +/* Select the lowest level debug interface available */ + +#ifdef CONFIG_CPP_HAVE_VARARGS +# ifdef CONFIG_ARCH_LOWPUTC +# define message(format, ...) lowsyslog(format, ##__VA_ARGS__) +# else +# define message(format, ...) syslog(format, ##__VA_ARGS__) +# endif +#else +# ifdef CONFIG_ARCH_LOWPUTC +# define message lowsyslog +# else +# define message syslog +# endif +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: iob_dump + * + * Description: + * Dump the contents of a I/O buffer chain + * + ****************************************************************************/ + +void iob_dump(FAR const char *msg, FAR struct iob_s *iob) +{ + FAR struct iob_s *head = iob; + FAR const uint8_t *buffer; + uint8_t data[32]; + unsigned int nbytes; + unsigned int i; + unsigned int j; + int len; + + message("%s: IOB=%p pktlen=%d\n", msg, head, head->io_pktlen); + + buffer = &iob->io_data[iob->io_offset]; + len = iob->io_len; + + for (i = 0; i < head->io_pktlen && iob; i += 32) + { + /* Copy 32-bytes into our local buffer */ + + for (nbytes = 0; nbytes < 32; nbytes++) + { + data[nbytes] = *buffer++; + + /* If we have exhausted the data in this I/O buffer, + * then skip to the next I/O buffer in the chain. + */ + + if (--len <= 0) + { + iob = iob->io_flink; + if (!iob) + { + /* Ooops... we are at the end of the chain. + * break out with iob = NULL, len == 0, and + * nbytes <= 32. + */ + + len = 0; + break; + } + + /* Get the data from the next I/O buffer in the chain */ + + buffer = &iob->io_data[iob->io_offset]; + len = iob->io_len; + } + } + + /* Make sure that we have something to print */ + + if (nbytes > 0) + { + message("%04x: ", i); + for (j = 0; j < 32; j++) + { + if (j == 16) + { + message(" "); + } + + if (i + j < head->io_pktlen) + { + message("%02x", buffer[j]); + } + else + { + message(" "); + } + } + + message(" "); + for (j = 0; j < 32; j++) + { + if (j == 16) + { + message(" "); + } + + if (i + j < head->io_pktlen) + { + if (buffer[j] >= 0x20 && buffer[j] < 0x7f) + { + message("%c", buffer[j]); + } + else + { + message("."); + } + } + } + + message("\n"); + } + } +} + +#endif /* CONFIG_DEBUG */ diff --git a/nuttx/net/iob/iob_free.c b/nuttx/net/iob/iob_free.c index 75d70e080..e2c95aae0 100644 --- a/nuttx/net/iob/iob_free.c +++ b/nuttx/net/iob/iob_free.c @@ -39,6 +39,7 @@ #include <nuttx/config.h> +#include <semaphore.h> #include <assert.h> #include <nuttx/arch.h> @@ -86,7 +87,6 @@ FAR struct iob_s *iob_free(FAR struct iob_s *iob) if (next) { - /* Copy and decrement the total packet length, being careful to * do nothing too crazy. */ @@ -115,8 +115,12 @@ FAR struct iob_s *iob_free(FAR struct iob_s *iob) */ flags = irqsave(); - iob->io_flink = g_iob_freelist; + iob->io_flink = g_iob_freelist; g_iob_freelist = iob; + + /* Signal that an IOB is available */ + + sem_post(&g_iob_sem); irqrestore(flags); /* And return the I/O buffer after the one that was freed */ diff --git a/nuttx/net/iob/iob_free_chain.c b/nuttx/net/iob/iob_free_chain.c index db9fc408c..320cdfd07 100644 --- a/nuttx/net/iob/iob_free_chain.c +++ b/nuttx/net/iob/iob_free_chain.c @@ -75,20 +75,12 @@ void iob_free_chain(FAR struct iob_s *iob) { - FAR struct iob_s *last; - irqstate_t flags; + FAR struct iob_s *next; - /* Find the last entry in the I/O buffer list */ + /* Free each IOB in the chain -- one at a time to keep the count straight */ - for (last = iob; last->io_flink; last = last->io_flink); - - /* Free the I/O buffer chain by adding it to the head of the free list. We - * don't know what context we are called from so we use extreme measures to - * protect the free list: We disable interrupts very briefly. - */ - - flags = irqsave(); - last->io_flink = g_iob_freelist; - g_iob_freelist = iob; - irqrestore(flags); + for (; iob; iob = next) + { + next = iob_free(iob); + } } diff --git a/nuttx/net/iob/iob_free_qentry.c b/nuttx/net/iob/iob_free_qentry.c index 55d4cf848..db0f3d78a 100644 --- a/nuttx/net/iob/iob_free_qentry.c +++ b/nuttx/net/iob/iob_free_qentry.c @@ -39,6 +39,7 @@ #include <nuttx/config.h> +#include <semaphore.h> #include <assert.h> #include <nuttx/arch.h> @@ -46,6 +47,8 @@ #include "iob.h" +#if CONFIG_IOB_NCHAINS > 0 + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -70,7 +73,7 @@ * Name: iob_free_qentry * * Description: - * Free the I/O buffer chain container by returning it to the free list. + * Free the I/O buffer chain container by returning it to the free list. * The link to the next I/O buffer in the chain is return. * ****************************************************************************/ @@ -86,11 +89,17 @@ FAR struct iob_qentry_s *iob_free_qentry(FAR struct iob_qentry_s *iobq) */ flags = irqsave(); - iobq->qe_flink = g_iob_freeqlist; + iobq->qe_flink = g_iob_freeqlist; g_iob_freeqlist = iobq; + + /* Signal that an I/O buffer chain container is available */ + + sem_post(&g_qentry_sem); irqrestore(flags); /* And return the I/O buffer chain container after the one that was freed */ return nextq; } + +#endif /* CONFIG_IOB_NCHAINS > 0 */ diff --git a/nuttx/net/iob/iob_free_queue.c b/nuttx/net/iob/iob_free_queue.c index 1b1a51888..d9af47d6c 100644 --- a/nuttx/net/iob/iob_free_queue.c +++ b/nuttx/net/iob/iob_free_queue.c @@ -45,6 +45,8 @@ #include "iob.h" +#if CONFIG_IOB_NCHAINS > 0 + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -112,3 +114,5 @@ void iob_free_queue(FAR struct iob_queue_s *qhead) iob_free_chain(iob); } } + +#endif /* CONFIG_IOB_NCHAINS > 0 */ diff --git a/nuttx/net/iob/iob_initialize.c b/nuttx/net/iob/iob_initialize.c index 5c4c122c3..2a9b527c0 100644 --- a/nuttx/net/iob/iob_initialize.c +++ b/nuttx/net/iob/iob_initialize.c @@ -40,6 +40,7 @@ #include <nuttx/config.h> #include <stdbool.h> +#include <semaphore.h> #include <nuttx/net/iob.h> @@ -60,7 +61,9 @@ /* This is a pool of pre-allocated I/O buffers */ static struct iob_s g_iob_pool[CONFIG_IOB_NBUFFERS]; +#if CONFIG_IOB_NCHAINS > 0 static struct iob_qentry_s g_iob_qpool[CONFIG_IOB_NCHAINS]; +#endif /**************************************************************************** * Public Data @@ -72,7 +75,16 @@ FAR struct iob_s *g_iob_freelist; /* A list of all free, unallocated I/O buffer queue containers */ +#if CONFIG_IOB_NCHAINS > 0 FAR struct iob_qentry_s *g_iob_freeqlist; +#endif + +/* Counting semaphores that tracks the number of free IOBs/qentries */ + +sem_t g_iob_sem; +#if CONFIG_IOB_NCHAINS > 0 +sem_t g_qentry_sem; +#endif /**************************************************************************** * Public Functions @@ -107,6 +119,9 @@ void iob_initialize(void) g_iob_freelist = iob; } + sem_init(&g_iob_sem, 0, CONFIG_IOB_NBUFFERS); + +#if CONFIG_IOB_NCHAINS > 0 /* Add each I/O buffer chain queue container to the free list */ for (i = 0; i < CONFIG_IOB_NCHAINS; i++) @@ -119,6 +134,8 @@ void iob_initialize(void) g_iob_freeqlist = iobq; } + sem_init(&g_qentry_sem, 0, CONFIG_IOB_NCHAINS); +#endif initialized = true; } } diff --git a/nuttx/net/iob/iob_remove_queue.c b/nuttx/net/iob/iob_remove_queue.c index 7a64d47c7..88752a2e8 100644 --- a/nuttx/net/iob/iob_remove_queue.c +++ b/nuttx/net/iob/iob_remove_queue.c @@ -45,6 +45,8 @@ #include "iob.h" +#if CONFIG_IOB_NCHAINS > 0 + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -92,3 +94,4 @@ FAR struct iob_s *iob_remove_queue(FAR struct iob_queue_s *iobq) return iob; } +#endif /* CONFIG_IOB_NCHAINS > 0 */ diff --git a/nuttx/net/net_send_buffered.c b/nuttx/net/net_send_buffered.c index 3717b609b..87f3d61d0 100644 --- a/nuttx/net/net_send_buffered.c +++ b/nuttx/net/net_send_buffered.c @@ -64,6 +64,7 @@ #include <arch/irq.h> #include <nuttx/clock.h> #include <nuttx/net/arp.h> +#include <nuttx/net/iob.h> #include <nuttx/net/uip/uip-arch.h> #include "net_internal.h" @@ -92,8 +93,8 @@ * ascending order of sequence number. * * Parameters: - * segment The segment to be inserted - * q The write buffer queue in which to insert the segment + * wrb The segment to be inserted + * q The write buffer queue in which to insert the segment * * Returned Value: * None @@ -103,17 +104,17 @@ * ****************************************************************************/ -static void send_insert_seqment(FAR struct tcp_wrbuffer_s *segment, +static void send_insert_seqment(FAR struct tcp_wrbuffer_s *wrb, FAR sq_queue_t *q) { - sq_entry_t *entry = (sq_entry_t*)segment; + sq_entry_t *entry = (sq_entry_t*)wrb; sq_entry_t *insert = NULL; sq_entry_t *itr; for (itr = sq_peek(q); itr; itr = sq_next(itr)) { - FAR struct tcp_wrbuffer_s *segment0 = (FAR struct tcp_wrbuffer_s*)itr; - if (segment0->wb_seqno < segment->wb_seqno) + FAR struct tcp_wrbuffer_s *wrb0 = (FAR struct tcp_wrbuffer_s*)itr; + if (WRB_SEQNO(wrb0) < WRB_SEQNO(wrb)) { insert = itr; } @@ -134,6 +135,54 @@ static void send_insert_seqment(FAR struct tcp_wrbuffer_s *segment, } /**************************************************************************** + * Function: lost_connection + * + * Description: + * The TCP connection has been lost. Free all write buffers. + * + * Parameters: + * psock The socket structure + * conn The connection structure associated with the socket + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void lost_connection(FAR struct socket *psock, + FAR struct uip_conn *conn) +{ + FAR sq_entry_t *entry; + FAR sq_entry_t *next; + + /* Do not allow any further callbacks */ + + psock->s_sndcb->flags = 0; + psock->s_sndcb->event = NULL; + + /* Free all queued write buffers */ + + for (entry = sq_peek(&conn->unacked_q); entry; entry = next) + { + next = sq_next(entry); + tcp_wrbuffer_release((FAR struct tcp_wrbuffer_s *)entry); + } + + for (entry = sq_peek(&conn->write_q); entry; entry = next) + { + next = sq_next(entry); + tcp_wrbuffer_release((FAR struct tcp_wrbuffer_s *)entry); + } + + /* Reset write buffering variables */ + + sq_init(&conn->unacked_q); + sq_init(&conn->write_q); + conn->expired = 0; + conn->sent = 0; +} + +/**************************************************************************** * Function: send_interrupt * * Description: @@ -156,10 +205,10 @@ static void send_insert_seqment(FAR struct tcp_wrbuffer_s *segment, static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn, FAR void *pvpriv, uint16_t flags) { - FAR struct uip_conn *conn = (FAR struct uip_conn*)pvconn; + FAR struct uip_conn *conn = (FAR struct uip_conn *)pvconn; FAR struct socket *psock = (FAR struct socket *)pvpriv; - nllvdbg("flags: %04x\n", flags); + //nllvdbg("flags: %04x\n", flags); /* If this packet contains an acknowledgement, then update the count of * acknowledged bytes. @@ -167,30 +216,101 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn, if ((flags & UIP_ACKDATA) != 0) { + FAR struct tcp_wrbuffer_s *wrb; FAR sq_entry_t *entry; FAR sq_entry_t *next; - FAR struct tcp_wrbuffer_s *segment; uint32_t ackno; ackno = uip_tcpgetsequence(TCPBUF->ackno); + nllvdbg("ACK: ackno=%d flags=%04x\n", ackno, flags); + + /* Look at every write buffer int he unacked_q. The unacked_q + * holds write buffers that have been entirely sent, but which + * have not yet been acked. + */ + for (entry = sq_peek(&conn->unacked_q); entry; entry = next) { - next = sq_next(entry); - segment = (FAR struct tcp_wrbuffer_s*)entry; + uint32_t lastseq; + + /* Check of some or all of this write buffer has been ACKed. */ + + next = sq_next(entry); + wrb = (FAR struct tcp_wrbuffer_s*)entry; + + /* If the ACKed sequence number is greater than the start + * sequence number of the write buffer, then some or all of + * the write buffer has been ACKed. + */ - if (segment->wb_seqno < ackno) + if (ackno > WRB_SEQNO(wrb)) { - nllvdbg("ACK: acked=%d buflen=%d ackno=%d\n", - segment->wb_seqno, segment->wb_nbytes, ackno); + /* Get the sequence number at the end of the data */ + + lastseq = WRB_SEQNO(wrb) + WRB_PKTLEN(wrb); + nllvdbg("ACK: seqno=%d lastseq=%d pktlen=%d ackno=%d\n", + WRB_SEQNO(wrb), lastseq, WRB_PKTLEN(wrb), ackno); + + /* Has the entire buffer been ACKed? */ + + if (ackno >= lastseq) + { + /* Yes... Remove the write buffer from ACK waiting queue */ + + sq_rem(entry, &conn->unacked_q); + + /* And return the write buffer to the pool of free buffers */ + + tcp_wrbuffer_release(wrb); + } + else + { + /* No, then just trim the ACKed bytes from the beginning + * of the write buffer. This will free up some I/O buffers + * that can be reused while are still sending the last + * buffers in the chain. + */ - /* Segment was ACKed. Remove from ACK waiting queue */ + WRB_TRIM(wrb, ackno - WRB_SEQNO(wrb)); + WRB_SEQNO(wrb) = ackno; - sq_rem(entry, &conn->unacked_q); + nllvdbg("ACK: seqno=%d pktlen=%d\n", + WRB_SEQNO(wrb), WRB_PKTLEN(wrb)); + } + } + } + + /* A special case is the head of the write_q which may be partially + * sent and so can still have un-ACKed bytes that could get ACKed + * before the entire write buffer has even been sent. + */ + + wrb = (FAR struct tcp_wrbuffer_s*)sq_peek(&conn->write_q); + if (wrb && WRB_SENT(wrb) > 0 && ackno > WRB_SEQNO(wrb)) + { + uint32_t nacked; - /* Return the write buffer to the pool of free buffers */ + /* Get the sequence number at the end of the data */ - tcp_wrbuffer_release(segment); + nacked = ackno - WRB_SEQNO(wrb); + if (nacked > WRB_SENT(wrb)) + { + /* More data has been ACKed then we have sent? */ + + nacked = WRB_SENT(wrb); } + + nllvdbg("ACK: seqno=%d nacked=%d sent=%d ackno=%d\n", + WRB_SEQNO(wrb), nacked, WRB_SENT(wrb), ackno); + + /* Trim the ACKed bytes from the beginning of the write buffer. */ + + WRB_TRIM(wrb, nacked); + WRB_SEQNO(wrb) = ackno; + WRB_SENT(wrb) -= nacked; + + nllvdbg("ACK: seqno=%d pktlen=%d sent=%d\n", + WRB_SEQNO(wrb), WRB_PKTLEN(wrb), WRB_SENT(wrb)); } } @@ -198,35 +318,53 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn, else if ((flags & (UIP_CLOSE | UIP_ABORT | UIP_TIMEDOUT)) != 0) { + nllvdbg("Lost connection: %04x\n", flags); + /* Report not connected */ - nllvdbg("Lost connection\n"); - net_lostconnection(psock, flags); - goto end_wait; - } + net_lostconnection(psock, flags); + + /* Free write buffers and terminate polling */ + + lost_connection(psock, conn); + return flags; + } /* Check if we are being asked to retransmit data */ else if ((flags & UIP_REXMIT) != 0) { - sq_entry_t *entry; + FAR struct tcp_wrbuffer_s *wrb; + FAR sq_entry_t *entry; - /* Put all segments that have been sent but not ACKed to write queue - * again note, the un-ACKed segment is put at the first of the write_q, - * so it can be sent as soon as possible. + nllvdbg("REXMIT: %04x\n", flags); + + /* If there is a partially sent write buffer at the head of the + * write_q, reset the number of bytes sent. */ - while ((entry = sq_remlast(&conn->unacked_q))) + wrb = (FAR struct tcp_wrbuffer_s*)sq_peek(&conn->write_q); + if (wrb) { - struct tcp_wrbuffer_s *segment = (struct tcp_wrbuffer_s*)entry; + WRB_SENT(wrb) = 0; + } + + /* Move all segments that have been sent but not ACKed to the write + * queue again note, the un-ACKed segments are put at the head of the + * write_q so they can be resent as soon as possible. + */ - if (segment->wb_nrtx >= UIP_MAXRTX) - { - //conn->unacked -= segment->wb_nbytes; + while ((entry = sq_remlast(&conn->unacked_q)) != NULL) + { + wrb = (FAR struct tcp_wrbuffer_s*)entry; + + /* Free any write buffers that have exceed the retry count */ - /* Return the write buffer */ + if (WRB_NRTX(wrb) >= UIP_MAXRTX) + { + /* Return the write buffer to the free list */ - tcp_wrbuffer_release(segment); + tcp_wrbuffer_release(wrb); /* NOTE expired is different from un-ACKed, it is designed to * represent the number of segments that have been sent, @@ -239,8 +377,16 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn, conn->expired++; continue; } + else + { + /* Insert the write buffer into the write_q (in sequence + * number order). The retransmission will occur below + * when the write buffer with the lowest sequenc number + * is pulled from the write_q again. + */ - send_insert_seqment(segment, &conn->write_q); + send_insert_seqment(wrb, &conn->write_q); + } } } @@ -259,7 +405,7 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn, * asked to retransmit data, (3) the connection is still healthy, and (4) * the outgoing packet is available for our use. In this case, we are * now free to send more data to receiver -- UNLESS the buffer contains - * unprocesed incoming data. In that event, we will have to wait for the + * unprocessed incoming data. In that event, we will have to wait for the * next polling cycle. */ @@ -283,44 +429,64 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn, if (arp_find(conn->ripaddr) != NULL) #endif { - FAR struct tcp_wrbuffer_s *segment; - FAR void *sndbuf; + FAR struct tcp_wrbuffer_s *wrb; size_t sndlen; - /* Get the amount of data that we can send in the next packet */ + /* Peek at the head of the write queue (but don't remove anything + * from the write queue yet. + */ - segment = (FAR struct tcp_wrbuffer_s *)sq_remfirst(&conn->write_q); - if (segment) + wrb = (FAR struct tcp_wrbuffer_s *)sq_peek(&conn->write_q); + if (wrb) { - sndbuf = segment->wb_buffer; - sndlen = segment->wb_nbytes; + /* Get the amount of data that we can send in the next packet. + * We will send either the remaining data in the buffer I/O + * buffer chain, or as much as will fit given the MSS and current + * window size. + */ - DEBUGASSERT(sndlen <= uip_mss(conn)); + sndlen = WRB_PKTLEN(wrb) - WRB_SENT(wrb); + if (sndlen > uip_mss(conn)) + { + sndlen = uip_mss(conn); + } - /* REVISIT: There should be a check here to assure that we do - * not excced the window (conn->winsize). - */ + if (sndlen > conn->winsize) + { + sndlen = conn->winsize; + } - /* Set the sequence number for this segment. NOTE: uIP - * updates sndseq on receipt of ACK *before* this function - * is called. In that case sndseq will point to the next - * unacknowledged byte (which might have already been - * sent). We will overwrite the value of sndseq here - * before the packet is sent. + nllvdbg("pktlen=%d sent=%d sndlen=%d\n", + WRB_PKTLEN(wrb), WRB_SENT(wrb), sndlen); + + /* Is this the first we have tried to send from this + * write buffer? */ - if (segment->wb_nrtx == 0 && segment->wb_seqno == (unsigned)-1) + if (WRB_SENT(wrb) == 0) { - segment->wb_seqno = conn->isn + conn->sent; - } + /* Yes..Set the sequence number for this segment. + * NOTE: the TCP stack updates sndseq on receipt of ACK + * *before* this function is called. In that case sndseq + * will point to the next unacknowledged byte (which might + * have already been sent). We will overwrite the value of + * sndseq here before the packet is sent. + */ - uip_tcpsetsequence(conn->sndseq, segment->wb_seqno); + if (WRB_NRTX(wrb) == 0 && WRB_SEQNO(wrb) == (unsigned)-1) + { + WRB_SEQNO(wrb) = conn->isn + conn->sent; + } + + uip_tcpsetsequence(conn->sndseq, WRB_SEQNO(wrb)); + } - /* Then set-up to send that amount of data. (this won't - * actually happen until the polling cycle completes). + /* Then set-up to send that amount of data with the offset + * corresponding to the amount of data already sent. (this + * won't* actually happen until the polling cycle completes). */ - uip_send(dev, sndbuf, sndlen); + uip_iobsend(dev, WRB_IOB(wrb), sndlen, WRB_SENT(wrb)); /* Remember how much data we send out now so that we know * when everything has been acknowledged. Just increment @@ -330,7 +496,7 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn, * this path. */ - if (segment->wb_nrtx == 0) + if (WRB_NRTX(wrb) == 0) { conn->unacked += sndlen; conn->sent += sndlen; @@ -343,11 +509,31 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn, * second interval before expiration. */ - segment->wb_nrtx++; + WRB_NRTX(wrb)++; + nllvdbg("nrtx=%d unacked=%d sent=%d\n", + WRB_NRTX(wrb), conn->unacked, conn->sent); - /* The segment is waiting for ACK again */ + /* Remove the write buffer from the write queue if the + * last of the data has been sent from the buffer. + */ + + WRB_SENT(wrb) += sndlen; + DEBUGASSERT(WRB_SENT(wrb) <= WRB_PKTLEN(wrb)); + + if (WRB_SENT(wrb) >= WRB_PKTLEN(wrb)) + { + FAR struct tcp_wrbuffer_s *tmp; - send_insert_seqment(segment, &conn->unacked_q); + tmp = (FAR struct tcp_wrbuffer_s *)sq_remfirst(&conn->write_q); + DEBUGASSERT(tmp == wrb); + UNUSED(tmp); + + /* Put the I/O buffer chin in the unacked queue; the + * segment is waiting for ACK again + */ + + send_insert_seqment(wrb, &conn->unacked_q); + } /* Only one data can be sent by low level driver at once, * tell the caller stop polling the other connection. @@ -361,15 +547,6 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn, /* Continue waiting */ return flags; - -end_wait: - - /* Do not allow any further callbacks */ - - psock->s_sndcb->flags = 0; - psock->s_sndcb->event = NULL; - - return flags; } /**************************************************************************** @@ -444,7 +621,7 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len, int flags) { uip_lock_t save; - ssize_t completed = 0; + ssize_t result = 0; int err; int ret = OK; @@ -477,16 +654,19 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len, psock->s_sndcb = uip_tcpcallbackalloc(conn); } - /* Test if the callback has been allocated*/ + /* Test if the callback has been allocated */ if (!psock->s_sndcb) { /* A buffer allocation error occurred */ - completed = -ENOMEM; + ndbg("ERROR: Failed to allocate callback\n"); + result = -ENOMEM; } else { + FAR struct tcp_wrbuffer_s *wrb = tcp_wrbuffer_alloc(); + /* Set up the callback in the connection */ psock->s_sndcb->flags = (UIP_ACKDATA | UIP_REXMIT |UIP_POLL | \ @@ -494,47 +674,35 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len, psock->s_sndcb->priv = (void*)psock; psock->s_sndcb->event = send_interrupt; - while (completed < len) - { - FAR struct tcp_wrbuffer_s *segment = tcp_wrbuffer_alloc(NULL); - if (segment) - { - size_t cnt; - - segment->wb_seqno = (unsigned)-1; - segment->wb_nrtx = 0; + /* Allocate an write buffer */ - if (len - completed > CONFIG_NET_TCP_WRITE_BUFSIZE) - { - cnt = CONFIG_NET_TCP_WRITE_BUFSIZE; - } - else - { - cnt = len - completed; - } + wrb = tcp_wrbuffer_alloc(); + if (wrb) + { + /* Initialize the write buffer */ - segment->wb_nbytes = cnt; - memcpy(segment->wb_buffer, (char*)buf + completed, cnt); - completed += cnt; + WRB_SEQNO(wrb) = (unsigned)-1; + WRB_NRTX(wrb) = 0; + WRB_COPYIN(wrb, (FAR uint8_t *)buf, len); - /* send_interrupt() will refer to all the write buffer by - * conn->writebuff - */ + /* send_interrupt() will send data in FIFO order from the + * conn->write_q + */ - sq_addlast(&segment->wb_node, &conn->write_q); + sq_addlast(&wrb->wb_node, &conn->write_q); - /* Notify the device driver of the availability of TX data */ + /* Notify the device driver of the availability of TX data */ - netdev_txnotify(conn->ripaddr); - } + netdev_txnotify(conn->ripaddr); + result = len; + } - /* A buffer allocation error occurred */ + /* A buffer allocation error occurred */ - else - { - completed = -ENOMEM; - break; - } + else + { + ndbg("ERROR: Failed to allocate write buffer\n"); + result = -ENOMEM; } } } @@ -549,9 +717,9 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len, * for the send length */ - if (completed < 0) + if (result < 0) { - err = completed; + err = result; goto errout; } @@ -567,7 +735,7 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len, /* Return the number of bytes actually sent */ - return completed; + return result; errout: set_errno(err); diff --git a/nuttx/net/net_send_unbuffered.c b/nuttx/net/net_send_unbuffered.c index 10c623ddb..d9d3dc9c9 100644 --- a/nuttx/net/net_send_unbuffered.c +++ b/nuttx/net/net_send_unbuffered.c @@ -467,7 +467,6 @@ static uint16_t tcpsend_interrupt(FAR struct uip_driver_s *dev, uint32_t sndlen = pstate->snd_buflen - pstate->snd_sent; - #if defined(CONFIG_NET_TCP_SPLIT) /* RFC 1122 states that a host may delay ACKing for up to 500ms but diff --git a/nuttx/net/tcp/Kconfig b/nuttx/net/tcp/Kconfig index 1d41e762a..64e13781d 100644 --- a/nuttx/net/tcp/Kconfig +++ b/nuttx/net/tcp/Kconfig @@ -82,6 +82,7 @@ endif # NET_TCP_READAHEAD config NET_TCP_WRITE_BUFFERS bool "Enable TCP/IP write buffering" default n + select NET_IOB ---help--- Write buffers allows buffering of ongoing TCP/IP packets, providing for higher performance, streamed output. @@ -91,34 +92,15 @@ config NET_TCP_WRITE_BUFFERS if NET_TCP_WRITE_BUFFERS -config NET_TCP_WRITE_BUFSIZE - int "TCP/IP write buffer size" - default 1220 if !NET_SLIP && NET_IPv6 - default 536 if !NET_SLIP && !NET_IPv6 - default 256 if NET_SLIP && !NET_IPv6 - ---help--- - Write buffers allows buffering of ongoing TCP/IP packets, providing - for higher performance, streamed output. - - The size of the write buffer will determine the maximum size of an - outgoing TCP packet payload (MSS). This value should NOT exceed the - maximum MSS which is determined by NET_BUFSIZE minus the size of - TCP, IP, and Ethernet headers (assuming you are using the Ethernet - transport). IPv4 hosts are required to be able to handle an MSS - of 536 octets and IPv6 hosts are required to be able to handle an - MSS of 1220 octets. - - This setting specifies the size of one TCP/IP write buffer. This - should best be a equal to the maximum packet size (NET_BUFSIZE). - -config NET_NTCP_WRITE_BUFFERS - int "Number of TCP/IP write buffers" +config NET_TCP_NWRBCHAINS + int "Number of pre-allocated I/O buffer chain heads" default 8 ---help--- - Write buffers allows buffering of ongoing TCP/IP packets, providing - for higher performance, streamed output. - - This setting specifies the number of TCP/IP write buffers. + These tiny nodes are used as "containers" to support queueing of + TCP write buffers. This setting will limit the number of TCP write + operations that can be "in-flight" at any give time. So a good + choice for this value would be the same as the maximum number of + TCP connections. config NET_TCP_WRBUFFER_DEBUG bool "Force write buffer debug" diff --git a/nuttx/net/tcp/Make.defs b/nuttx/net/tcp/Make.defs index fa1b24e46..b70b90ec9 100644 --- a/nuttx/net/tcp/Make.defs +++ b/nuttx/net/tcp/Make.defs @@ -51,6 +51,9 @@ endif ifeq ($(CONFIG_NET_TCP_WRITE_BUFFERS),y) NET_CSRCS += tcp_wrbuffer.c +ifeq ($(CONFIG_DEBUG),y) +NET_CSRCS += tcp_wrbuffer_dump.c +endif endif # Include TCP build support diff --git a/nuttx/net/tcp/tcp.h b/nuttx/net/tcp/tcp.h index b2b515793..a18b78a6c 100644 --- a/nuttx/net/tcp/tcp.h +++ b/nuttx/net/tcp/tcp.h @@ -98,10 +98,8 @@ void tcp_wrbuffer_initialize(void); #ifdef CONFIG_NET_TCP_WRITE_BUFFERS struct tcp_wrbuffer_s; -struct timespec; -FAR struct tcp_wrbuffer_s * -tcp_wrbuffer_alloc(FAR const struct timespec *abstime); +FAR struct tcp_wrbuffer_s *tcp_wrbuffer_alloc(void); #endif /* CONFIG_NET_TCP_WRITE_BUFFERS */ /**************************************************************************** @@ -118,7 +116,23 @@ tcp_wrbuffer_alloc(FAR const struct timespec *abstime); ****************************************************************************/ #ifdef CONFIG_NET_TCP_WRITE_BUFFERS -void tcp_wrbuffer_release(FAR struct tcp_wrbuffer_s *wrbuffer); +void tcp_wrbuffer_release(FAR struct tcp_wrbuffer_s *wrb); +#endif /* CONFIG_NET_TCP_WRITE_BUFFERS */ + +/**************************************************************************** + * Function: tcp_writebuffer_dump + * + * Description: + * Dump the contents of a write buffer. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_TCP_WRITE_BUFFERS +#ifdef CONFIG_DEBUG +void tcp_writebuffer_dump(FAR const char *msg, FAR struct tcp_wrbuffer_s *wrb); +#else +# define tcp_writebuffer_dump(msg,wrb) +#endif #endif /* CONFIG_NET_TCP_WRITE_BUFFERS */ #undef EXTERN diff --git a/nuttx/net/tcp/tcp_wrbuffer.c b/nuttx/net/tcp/tcp_wrbuffer.c index cba5f5d23..929b2f3a2 100644 --- a/nuttx/net/tcp/tcp_wrbuffer.c +++ b/nuttx/net/tcp/tcp_wrbuffer.c @@ -50,9 +50,13 @@ #include <queue.h> #include <semaphore.h> +#include <string.h> +#include <assert.h> #include <debug.h> -#include "uip/uip_internal.h" +#include "tcp/tcp.h" +#include "nuttx/net/iob.h" +#include "nuttx/net/uip/uip-tcp.h" /**************************************************************************** * Private Types @@ -72,7 +76,7 @@ struct wrbuffer_s /* These are the pre-allocated write buffers */ - struct tcp_wrbuffer_s buffers[CONFIG_NET_NTCP_WRITE_BUFFERS]; + struct tcp_wrbuffer_s buffers[CONFIG_NET_TCP_NWRBCHAINS]; }; /**************************************************************************** @@ -108,12 +112,12 @@ void tcp_wrbuffer_initialize(void) sq_init(&g_wrbuffer.freebuffers); - for (i = 0; i < CONFIG_NET_NTCP_WRITE_BUFFERS; i++) + for (i = 0; i < CONFIG_NET_TCP_NWRBCHAINS; i++) { sq_addfirst(&g_wrbuffer.buffers[i].wb_node, &g_wrbuffer.freebuffers); } - sem_init(&g_wrbuffer.sem, 0, CONFIG_NET_NTCP_WRITE_BUFFERS); + sem_init(&g_wrbuffer.sem, 0, CONFIG_NET_TCP_NWRBCHAINS); } /**************************************************************************** @@ -129,26 +133,39 @@ void tcp_wrbuffer_initialize(void) * ****************************************************************************/ -FAR struct tcp_wrbuffer_s * -tcp_wrbuffer_alloc(FAR const struct timespec *abstime) +FAR struct tcp_wrbuffer_s *tcp_wrbuffer_alloc(void) { - int ret; + FAR struct tcp_wrbuffer_s *wrb; - if (abstime) - { - ret = sem_timedwait(&g_wrbuffer.sem, abstime); - } - else - { - ret = sem_wait(&g_wrbuffer.sem); - } + /* We need to allocate two things: (1) A write buffer structure and (2) + * at least one I/O buffer to start the chain. + * + * Allocate the write buffer structure first then the IOBG. In order to + * avoid deadlocks, we will need to free the IOB first, then the write + * buffer + */ + + DEBUGVERIFY(sem_wait(&g_wrbuffer.sem)); + + /* Now, we are guaranteed to have a write buffer structure reserved + * for us in the free list. + */ - if (ret != 0) + wrb = (FAR struct tcp_wrbuffer_s *)sq_remfirst(&g_wrbuffer.freebuffers); + DEBUGASSERT(wrb); + memset(wrb, 0, sizeof(struct tcp_wrbuffer_s)); + + /* Now get the first I/O buffer for the write buffer structure */ + + wrb->wb_iob = iob_alloc(); + if (!wrb->wb_iob) { + ndbg("ERROR: Failed to allocate I/O buffer\n"); + tcp_wrbuffer_release(wrb); return NULL; } - return (FAR struct tcp_wrbuffer_s*)sq_remfirst(&g_wrbuffer.freebuffers); + return wrb; } /**************************************************************************** @@ -164,9 +181,19 @@ tcp_wrbuffer_alloc(FAR const struct timespec *abstime) * ****************************************************************************/ -void tcp_wrbuffer_release(FAR struct tcp_wrbuffer_s *wrbuffer) +void tcp_wrbuffer_release(FAR struct tcp_wrbuffer_s *wrb) { - sq_addlast(&wrbuffer->wb_node, &g_wrbuffer.freebuffers); + DEBUGASSERT(wrb && wrb->wb_iob); + + /* To avoid deadlocks, we must following this ordering: Release the I/O + * buffer chain first, then the write buffer structure. + */ + + iob_free_chain(wrb->wb_iob); + + /* Then free the write buffer structure */ + + sq_addlast(&wrb->wb_node, &g_wrbuffer.freebuffers); sem_post(&g_wrbuffer.sem); } diff --git a/nuttx/net/tcp/tcp_wrbuffer_dump.c b/nuttx/net/tcp/tcp_wrbuffer_dump.c new file mode 100644 index 000000000..910eeb304 --- /dev/null +++ b/nuttx/net/tcp/tcp_wrbuffer_dump.c @@ -0,0 +1,89 @@ +/**************************************************************************** + * net/tcp/tcp_wrbuffer_dump.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <debug.h> + +#include <nuttx/net/iob.h> +#include <nuttx/net/uip/uip-tcp.h> + +#ifdef CONFIG_DEBUG + +/**************************************************************************** + * Pre-processor definitions + ****************************************************************************/ + +/* Select the lowest level debug interface available */ + +#ifdef CONFIG_CPP_HAVE_VARARGS +# ifdef CONFIG_ARCH_LOWPUTC +# define message(format, ...) lowsyslog(format, ##__VA_ARGS__) +# else +# define message(format, ...) syslog(format, ##__VA_ARGS__) +# endif +#else +# ifdef CONFIG_ARCH_LOWPUTC +# define message lowsyslog +# else +# define message syslog +# endif +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: tcp_wrbuffer_dump + * + * Description: + * Dump the contents of a write buffer + * + ****************************************************************************/ + +void tcp_wrbuffer_dump(FAR const char *msg, FAR struct tcp_wrbuffer_s *wrb) +{ + message("%s: WRB=%p segno=%d sent=%d nrtx=%d\n", + msg, wrb, WRB_SEQNO(wrb), WRB_SENT(wrb), WRB_NRTX(wrb)); + iob_dump("I/O Buffer Chain", WRB_IOB(wrb)); +} + +#endif /* CONFIG_DEBUG */ diff --git a/nuttx/net/uip/Make.defs b/nuttx/net/uip/Make.defs index 8849e32fe..6b780152d 100644 --- a/nuttx/net/uip/Make.defs +++ b/nuttx/net/uip/Make.defs @@ -40,6 +40,12 @@ ifeq ($(CONFIG_NET),y) NET_CSRCS += uip_initialize.c uip_setipid.c uip_input.c uip_send.c NET_CSRCS += uip_poll.c uip_chksum.c uip_callback.c +# I/O buffer chain support required? + +ifeq ($(CONFIG_NET_IOB),y) +NET_CSRCS += uip_iobsend.c +endif + # Non-interrupt level support required? ifeq ($(CONFIG_NET_NOINTS),y) diff --git a/nuttx/net/uip/uip_iobsend.c b/nuttx/net/uip/uip_iobsend.c new file mode 100644 index 000000000..d5ed4072b --- /dev/null +++ b/nuttx/net/uip/uip_iobsend.c @@ -0,0 +1,105 @@ +/**************************************************************************** + * net/uip/uip_iobsend.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <string.h> +#include <assert.h> +#include <debug.h> + +#include <nuttx/net/iob.h> +#include <nuttx/net/uip/uip.h> +#include <nuttx/net/uip/uip-arch.h> + +#ifdef CONFIG_NET_IOB + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Type Declarations + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Global Constant Data + ****************************************************************************/ + +/**************************************************************************** + * Global Variables + ****************************************************************************/ + +/**************************************************************************** + * Private Constant Data + ****************************************************************************/ + +/**************************************************************************** + * Private Variables + ****************************************************************************/ + +/**************************************************************************** + * Global Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: uip_iobsend + * + * Description: + * Called from socket logic in response to a xmit or poll request from the + * the network interface driver. + * + * Assumptions: + * Called from the interrupt level or, at a minimum, with interrupts + * disabled. + * + ****************************************************************************/ + +void uip_iobsend(FAR struct uip_driver_s *dev, FAR struct iob_s *iob, + unsigned int len, unsigned int offset) +{ + DEBUGASSERT(dev && len > 0 && len < CONFIG_NET_BUFSIZE); + + iob_copyout(dev->d_snddata, iob, len, offset); + dev->d_sndlen = len; +} + +#endif /* CONFIG_NET_IOB */ + diff --git a/nuttx/net/uip/uip_send.c b/nuttx/net/uip/uip_send.c index 0b799f203..5a9015d89 100644 --- a/nuttx/net/uip/uip_send.c +++ b/nuttx/net/uip/uip_send.c @@ -1,7 +1,7 @@ /**************************************************************************** * net/uip/uip_send.c * - * Copyright (C) 2007i, 2008 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2008 Gregory Nutt. All rights reserved. * Author: Gregory Nutt <gnutt@nuttx.org> * * Based in part on uIP which also has a BSD stylie license: @@ -42,6 +42,7 @@ ****************************************************************************/ #include <string.h> +#include <assert.h> #include <debug.h> #include <nuttx/net/uip/uip.h> @@ -94,13 +95,8 @@ void uip_send(struct uip_driver_s *dev, const void *buf, int len) { - /* Some sanity checks -- note that the actually available length in the - * buffer is considerably less than CONFIG_NET_BUFSIZE. - */ + DEBUGASSERT(dev && len > 0 && len < CONFIG_NET_BUFSIZE); - if (dev && len > 0 && len < CONFIG_NET_BUFSIZE) - { - memcpy(dev->d_snddata, buf, len); - dev->d_sndlen = len; - } + memcpy(dev->d_snddata, buf, len); + dev->d_sndlen = len; } |