First cut at conversion of write-buffering to use I/O buffer chaings (IOBs)

This commit is contained in:
Gregory Nutt 2014-06-22 11:27:57 -06:00
parent 7e83501ce5
commit 356d25b503
27 changed files with 1083 additions and 234 deletions

View File

@ -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 */

View File

@ -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

View File

@ -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.
*

View File

@ -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.
*/

View File

@ -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

View File

@ -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

View File

@ -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
****************************************************************************/

View File

@ -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 */

View File

@ -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();
}
}

View File

@ -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
****************************************************************************/
@ -60,6 +65,104 @@
* Public Data
****************************************************************************/
/****************************************************************************
* 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 */

177
net/iob/iob_dump.c Normal file
View File

@ -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 */

View File

@ -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 */

View File

@ -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);
}
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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;
}
}

View File

@ -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 */

View File

@ -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;
}
@ -133,6 +134,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
*
@ -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,66 +216,155 @@ 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;
if (segment->wb_seqno < ackno)
/* 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 (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 */
/* Segment was ACKed. Remove from ACK waiting queue */
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);
sq_rem(entry, &conn->unacked_q);
/* Has the entire buffer been ACKed? */
/* Return the write buffer to the pool of free buffers */
if (ackno >= lastseq)
{
/* Yes... Remove the write buffer from ACK waiting queue */
tcp_wrbuffer_release(segment);
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.
*/
WRB_TRIM(wrb, ackno - WRB_SEQNO(wrb));
WRB_SEQNO(wrb) = ackno;
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;
/* Get the sequence number at the end of the data */
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));
}
}
/* Check for a loss of connection */
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)
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 */
if (WRB_NRTX(wrb) >= UIP_MAXRTX)
{
//conn->unacked -= segment->wb_nbytes;
/* Return the write buffer to the free list */
/* Return the write buffer */
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;
DEBUGASSERT(sndlen <= uip_mss(conn));
/* REVISIT: There should be a check here to assure that we do
* not excced the window (conn->winsize).
/* 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.
*/
/* 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.
*/
if (segment->wb_nrtx == 0 && segment->wb_seqno == (unsigned)-1)
sndlen = WRB_PKTLEN(wrb) - WRB_SENT(wrb);
if (sndlen > uip_mss(conn))
{
segment->wb_seqno = conn->isn + conn->sent;
sndlen = uip_mss(conn);
}
uip_tcpsetsequence(conn->sndseq, segment->wb_seqno);
if (sndlen > conn->winsize)
{
sndlen = conn->winsize;
}
/* Then set-up to send that amount of data. (this won't
* actually happen until the polling cycle completes).
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?
*/
uip_send(dev, sndbuf, sndlen);
if (WRB_SENT(wrb) == 0)
{
/* 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.
*/
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 with the offset
* corresponding to the amount of data already sent. (this
* won't* actually happen until the polling cycle completes).
*/
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.
*/
send_insert_seqment(segment, &conn->unacked_q);
WRB_SENT(wrb) += sndlen;
DEBUGASSERT(WRB_SENT(wrb) <= WRB_PKTLEN(wrb));
if (WRB_SENT(wrb) >= WRB_PKTLEN(wrb))
{
FAR struct tcp_wrbuffer_s *tmp;
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.
@ -360,15 +546,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)
/* Allocate an write buffer */
wrb = tcp_wrbuffer_alloc();
if (wrb)
{
FAR struct tcp_wrbuffer_s *segment = tcp_wrbuffer_alloc(NULL);
if (segment)
{
size_t cnt;
/* Initialize the write buffer */
segment->wb_seqno = (unsigned)-1;
segment->wb_nrtx = 0;
WRB_SEQNO(wrb) = (unsigned)-1;
WRB_NRTX(wrb) = 0;
WRB_COPYIN(wrb, (FAR uint8_t *)buf, len);
if (len - completed > CONFIG_NET_TCP_WRITE_BUFSIZE)
{
cnt = CONFIG_NET_TCP_WRITE_BUFSIZE;
}
else
{
cnt = len - completed;
}
/* send_interrupt() will send data in FIFO order from the
* conn->write_q
*/
segment->wb_nbytes = cnt;
memcpy(segment->wb_buffer, (char*)buf + completed, cnt);
completed += cnt;
sq_addlast(&wrb->wb_node, &conn->write_q);
/* send_interrupt() will refer to all the write buffer by
* conn->writebuff
*/
/* Notify the device driver of the availability of TX data */
sq_addlast(&segment->wb_node, &conn->write_q);
netdev_txnotify(conn->ripaddr);
result = len;
}
/* Notify the device driver of the availability of TX data */
/* A buffer allocation error occurred */
netdev_txnotify(conn->ripaddr);
}
/* 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);

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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
*/
if (ret != 0)
DEBUGVERIFY(sem_wait(&g_wrbuffer.sem));
/* Now, we are guaranteed to have a write buffer structure reserved
* for us in the free list.
*/
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);
}

View File

@ -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 */

View File

@ -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)

105
net/uip/uip_iobsend.c Normal file
View File

@ -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 */

View File

@ -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;
}