Add throttle support to the I/O buffer logic

This commit is contained in:
Gregory Nutt 2014-06-24 11:53:19 -06:00
parent f9f9244143
commit e9a588c398
25 changed files with 161 additions and 59 deletions

View File

@ -43,6 +43,7 @@
#include <nuttx/config.h>
#include <stdint.h>
#include <stdbool.h>
#include <nuttx/net/iob.h>
@ -51,6 +52,41 @@
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
/* I/O buffer allocation logic supports a throttle value for the TCP
* read-ahead buffering to prevent the read-ahead from consuming all
* available I/O buffers. This throttle only applies if both TCP write
* buffering and TCP read-ahead buffering are enabled.
*/
#if !defined(CONFIG_NET_TCP_WRITE_BUFFERS) || !defined(CONFIG_NET_TCP_READAHEAD)
# undef CONFIG_IOB_THROTTLE
# define CONFIG_IOB_THROTTLE 0
#endif
/* The correct way to disable throttling is to the the throttle value to
* zero.
*/
#if !defined(CONFIG_IOB_THROTTLE)
# define CONFIG_IOB_THROTTLE 0
#endif
/* Some I/O buffers should be allocated */
#if !defined(CONFIG_IOB_NBUFFERS)
# warning CONFIG_IOB_NBUFFERS not defined
# define CONFIG_IOB_NBUFFERS 0
#endif
#if CONFIG_IOB_NBUFFERS < 1
# error CONFIG_IOB_NBUFFERS is zero
#endif
#if CONFIG_IOB_NBUFFERS <= CONFIG_IOB_THROTTLE
# error CONFIG_IOB_NBUFFERS <= CONFIG_IOB_THROTTLE
#endif
/* IOB helpers */
@ -146,7 +182,7 @@ void iob_initialize(void);
*
****************************************************************************/
FAR struct iob_s *iob_alloc(void);
FAR struct iob_s *iob_alloc(bool throttled);
/****************************************************************************
* Name: iob_free
@ -217,7 +253,7 @@ void iob_free_queue(FAR struct iob_queue_s *qhead);
****************************************************************************/
int iob_copyin(FAR struct iob_s *iob, FAR const uint8_t *src,
unsigned int len, unsigned int offset);
unsigned int len, unsigned int offset, bool throttled);
/****************************************************************************
* Name: iob_copyout
@ -239,7 +275,7 @@ int iob_copyout(FAR uint8_t *dest, FAR const struct iob_s *iob,
*
****************************************************************************/
int iob_clone(FAR struct iob_s *iob1, FAR struct iob_s *iob2);
int iob_clone(FAR struct iob_s *iob1, FAR struct iob_s *iob2, bool throttled);
/****************************************************************************
* Name: iob_concat

View File

@ -133,7 +133,7 @@
# 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_COPYIN(wrb,src,n) (iob_copyin((wrb)->wb_iob,src,(n),0,false))
# define WRB_TRIM(wrb,n) \
do { (wrb)->wb_iob = iob_trimhead((wrb)->wb_iob,(n)); } while (0)

View File

@ -14,7 +14,9 @@ if NET_IOB
config IOB_NBUFFERS
int "Number of pre-allocated network I/O buffers"
default 24
default 24 if (NET_TCP_WRITE_BUFFERS && !NET_TCP_READAHEAD) || (!NET_TCP_WRITE_BUFFERS && NET_TCP_READAHEAD)
default 36 if NET_TCP_WRITE_BUFFERS && NET_TCP_READAHEAD
default 8 if !NET_TCP_WRITE_BUFFERS && !NET_TCP_READAHEAD
---help---
Each packet is represented by a series of small I/O buffers in a
chain. This setting determines the number of preallocated I/O
@ -30,7 +32,8 @@ config IOB_BUFSIZE
config IOB_NCHAINS
int "Number of pre-allocated I/O buffer chain heads"
default 0
default 0 if !NET_TCP_READAHEAD
default 8 if NET_TCP_READAHEAD
---help---
These tiny nodes are used as "containers" to support queueing of
I/O buffer chains. This will limit the number of I/O transactions
@ -42,7 +45,20 @@ config IOB_NCHAINS
I/O buffer chain containers that also carry a payload of usage
specific information.
config NET_IOB_DEBUG
config IOB_THROTTLE
int "I/O buffer throttle value"
default 0 if !NET_TCP_WRITE_BUFFERS || !NET_TCP_READAHEAD
default 8 if NET_TCP_WRITE_BUFFERS && !NET_TCP_READAHEAD
depends on NET_TCP_WRITE_BUFFERS && !NET_TCP_READAHEAD
---help---
TCP write buffering and read-ahead buffer use the same pool of free
I/O buffers. In order to prevent uncontrolled incoming TCP packets
from hogging all of the available, pre-allocated I/O buffers, a
throttling value is required. This throttle value assures that
I/O buffers will be denied to the read-ahead logic before TCP writes
are halted.
config IOB_DEBUG
bool "Force I/O buffer debug"
default n
depends on DEBUG

View File

@ -64,12 +64,19 @@ extern FAR struct iob_s *g_iob_freelist;
/* A list of all free, unallocated I/O buffer queue containers */
#if CONFIG_IOB_NCHAINS > 0
extern FAR struct iob_qentry_s *g_iob_freeqlist;
#endif
/* Counting semaphores that tracks the number of free IOBs/qentries */
extern sem_t g_iob_sem;
extern sem_t g_qentry_sem;
extern sem_t g_iob_sem; /* Counts free I/O buffers */
#if CONFIG_IOB_THROTTLE > 0
extern sem_t g_throttle_sem; /* Counts available I/O buffers when throttled */
#endif
#if CONFIG_IOB_NCHAINS > 0
extern sem_t g_qentry_sem; /* Counts free I/O buffer queue containers */
#endif
/****************************************************************************
* Public Data

View File

@ -39,7 +39,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
#if defined(CONFIG_DEBUG) && defined(CONFIG_IOB_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET

View File

@ -39,7 +39,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
#if defined(CONFIG_DEBUG) && defined(CONFIG_IOB_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET
@ -83,34 +83,59 @@
*
****************************************************************************/
static FAR struct iob_s *iob_tryalloc(void)
static FAR struct iob_s *iob_tryalloc(bool throttled)
{
FAR struct iob_s *iob;
irqstate_t flags;
#if CONFIG_IOB_THROTTLE > 0
FAR sem_t *sem;
int semcount;
#endif
#if CONFIG_IOB_THROTTLE > 0
/* Select the semaphore count to check. */
sem = (throttled ? : &g_throttled_sem, &g_iob_sem);
#endif
/* 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();
iob = g_iob_freelist;
if (iob)
#if CONFIG_IOB_THROTTLE > 0
/* If there are free I/O buffers for this allocation */
DEBUGVERIFY(sem_getvalue(sem, semcount));
if (semcount > 0)
#endif
{
/* Remove the I/O buffer from the free list and decrement the counting
* semaphore that tracks the number of free IOBs.
*/
/* Take the I/O buffer from the head of the free list */
g_iob_freelist = iob->io_flink;
DEBUGVERIFY(sem_trywait(&g_iob_sem));
irqrestore(flags);
iob = g_iob_freelist;
if (iob)
{
/* Remove the I/O buffer from the free list and decrement the
* counting semaphore(s) that tracks the number of available
* IOBs.
*/
/* Put the I/O buffer in a known state */
g_iob_freelist = iob->io_flink;
DEBUGVERIFY(sem_trywait(&g_iob_sem));
#if CONFIG_IOB_THROTTLE > 0
DEBUGVERIFY(sem_trywait(&g_throttle_sem));
#endif
irqrestore(flags);
iob->io_flink = NULL; /* Not in a chain */
iob->io_len = 0; /* Length of the data in the entry */
iob->io_offset = 0; /* Offset to the beginning of data */
iob->io_pktlen = 0; /* Total length of the packet */
return iob;
/* Put the I/O buffer in a known state */
iob->io_flink = NULL; /* Not in a chain */
iob->io_len = 0; /* Length of the data in the entry */
iob->io_offset = 0; /* Offset to the beginning of data */
iob->io_pktlen = 0; /* Total length of the packet */
return iob;
}
}
irqrestore(flags);
@ -126,12 +151,21 @@ static FAR struct iob_s *iob_tryalloc(void)
*
****************************************************************************/
static FAR struct iob_s *iob_allocwait(void)
static FAR struct iob_s *iob_allocwait(bool throttled)
{
FAR struct iob_s *iob;
irqstate_t flags;
FAR sem_t *sem;
int ret;
#if CONFIG_IOB_THROTTLE > 0
/* Select the semaphore count to check. */
sem = (throttled ? : &g_throttled_sem, &g_iob_sem);
#else
sem = &g_iob_sem;
#endif
/* 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
@ -145,7 +179,7 @@ static FAR struct iob_s *iob_allocwait(void)
* will be decremented atomically.
*/
iob = iob_tryalloc();
iob = iob_tryalloc(throttled);
if (!iob)
{
/* If not successful, then the semaphore count was less than or
@ -154,7 +188,7 @@ static FAR struct iob_s *iob_allocwait(void)
* count will be incremented.
*/
ret = sem_wait(&g_iob_sem);
ret = sem_wait(sem);
/* When we wake up from wait, an I/O buffer was returned to
* the free list. However, if there are concurrent allocations
@ -182,7 +216,7 @@ static FAR struct iob_s *iob_allocwait(void)
*
****************************************************************************/
FAR struct iob_s *iob_alloc(void)
FAR struct iob_s *iob_alloc(bool throttled)
{
/* Were we called from the interrupt level? */
@ -190,12 +224,12 @@ FAR struct iob_s *iob_alloc(void)
{
/* Yes, then try to allocate an I/O buffer without waiting */
return iob_tryalloc();
return iob_tryalloc(throttled);
}
else
{
/* Then allocate an I/O buffer, waiting as necessary */
return iob_allocwait();
return iob_allocwait(throttled);
}
}

View File

@ -39,7 +39,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
#if defined(CONFIG_DEBUG) && defined(CONFIG_IOB_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET

View File

@ -39,7 +39,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
#if defined(CONFIG_DEBUG) && defined(CONFIG_IOB_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET
@ -87,7 +87,7 @@
*
****************************************************************************/
int iob_clone(FAR struct iob_s *iob1, FAR struct iob_s *iob2)
int iob_clone(FAR struct iob_s *iob1, FAR struct iob_s *iob2, bool throttled)
{
FAR uint8_t *src;
FAR uint8_t *dest;
@ -177,7 +177,7 @@ int iob_clone(FAR struct iob_s *iob1, FAR struct iob_s *iob2)
* destination I/O buffer chain.
*/
next = iob_alloc();
next = iob_alloc(throttled);
if (!next)
{
ndbg("Failed to allocate an I/O buffer/n");

View File

@ -39,7 +39,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
#if defined(CONFIG_DEBUG) && defined(CONFIG_IOB_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET

View File

@ -39,7 +39,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
#if defined(CONFIG_DEBUG) && defined(CONFIG_IOB_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET

View File

@ -39,7 +39,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
#if defined(CONFIG_DEBUG) && defined(CONFIG_IOB_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET
@ -90,7 +90,7 @@
****************************************************************************/
int iob_copyin(FAR struct iob_s *iob, FAR const uint8_t *src,
unsigned int len, unsigned int offset)
unsigned int len, unsigned int offset, bool throttled)
{
FAR struct iob_s *head = iob;
FAR struct iob_s *next;
@ -206,7 +206,7 @@ int iob_copyin(FAR struct iob_s *iob, FAR const uint8_t *src,
{
/* Yes.. allocate a new buffer */
next = iob_alloc();
next = iob_alloc(throttled);
if (next == NULL)
{
ndbg("ERROR: Failed to allocate I/O buffer\n");

View File

@ -39,7 +39,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
#if defined(CONFIG_DEBUG) && defined(CONFIG_IOB_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET

View File

@ -39,7 +39,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
#if defined(CONFIG_DEBUG) && defined(CONFIG_IOB_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET

View File

@ -39,7 +39,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
#if defined(CONFIG_DEBUG) && defined(CONFIG_IOB_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET

View File

@ -39,7 +39,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
#if defined(CONFIG_DEBUG) && defined(CONFIG_IOB_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET

View File

@ -39,7 +39,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
#if defined(CONFIG_DEBUG) && defined(CONFIG_IOB_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET

View File

@ -39,7 +39,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
#if defined(CONFIG_DEBUG) && defined(CONFIG_IOB_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET
@ -88,9 +88,12 @@ FAR struct iob_qentry_s *g_iob_freeqlist;
/* Counting semaphores that tracks the number of free IOBs/qentries */
sem_t g_iob_sem;
sem_t g_iob_sem; /* Counts free I/O buffers */
#if CONFIG_IOB_THROTTLE > 0
sem_t g_throttle_sem; /* Counts available I/O buffers when throttled */
#endif
#if CONFIG_IOB_NCHAINS > 0
sem_t g_qentry_sem;
sem_t g_qentry_sem; /* Counts free I/O buffer queue containers */
#endif
/****************************************************************************

View File

@ -39,7 +39,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
#if defined(CONFIG_DEBUG) && defined(CONFIG_IOB_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET

View File

@ -39,7 +39,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
#if defined(CONFIG_DEBUG) && defined(CONFIG_IOB_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET

View File

@ -121,7 +121,7 @@ int main(int argc, char **argv)
int i;
iob_initialize();
iob = iob_alloc();
iob = iob_alloc(false);
for (i = 0; i < 4096; i++)
{
@ -129,11 +129,11 @@ int main(int argc, char **argv)
}
memset(buffer2, 0xff, 4096);
iob_copyin(iob, buffer2, 47, 0);
iob_copyin(iob, buffer2, 47, 0, false);
printf("Copy IN: 47, offset 0\n");
dump_chain(iob);
iob_copyin(iob, buffer1, 4096, 47);
iob_copyin(iob, buffer1, 4096, 47, false);
printf("Copy IN: 4096, offset 47\n");
dump_chain(iob);

View File

@ -39,7 +39,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
#if defined(CONFIG_DEBUG) && defined(CONFIG_IOB_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET

View File

@ -39,7 +39,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
#if defined(CONFIG_DEBUG) && defined(CONFIG_IOB_DEBUG)
/* Force debug output (from this file only) */
# undef CONFIG_DEBUG_NET

View File

@ -107,7 +107,7 @@ config NET_TCP_WRBUFFER_DEBUG
bool "Force write buffer debug"
default n
depends on DEBUG
select NET_IOB_DEBUG
select IOB_DEBUG
---help---
This option will force debug output from TCP write buffer logic,
even without network debug output. This is not normally something
@ -119,7 +119,7 @@ config NET_TCP_WRBUFFER_DUMP
bool "Force write buffer dump"
default n
depends on DEBUG_NET || NET_TCP_WRBUFFER_DEBUG
select NET_IOB_DEBUG
select IOB_DEBUG
---help---
Dump the contents of the write buffers. You do not want to do this
unless you really want to analyze the write buffer transfers in

View File

@ -153,6 +153,9 @@ void tcp_wrbuffer_initialize(void);
* the free list. This function is called from TCP logic when a buffer
* of TCP data is about to sent
*
* Input parameters:
* None
*
* Assumptions:
* Called from user logic with interrupts enabled.
*

View File

@ -128,6 +128,9 @@ void tcp_wrbuffer_initialize(void)
* the free list. This function is called from TCP logic when a buffer
* of TCP data is about to sent
*
* Input parameters:
* None
*
* Assumptions:
* Called from user logic with interrupts enabled.
*
@ -157,7 +160,7 @@ FAR struct tcp_wrbuffer_s *tcp_wrbuffer_alloc(void)
/* Now get the first I/O buffer for the write buffer structure */
wrb->wb_iob = iob_alloc();
wrb->wb_iob = iob_alloc(false);
if (!wrb->wb_iob)
{
ndbg("ERROR: Failed to allocate I/O buffer\n");