net: Optimize ipfwd and wrbuffer by buffer pool

To allow dynamic allocation of these structs, performance keeps the same
as the previous implementation.

Signed-off-by: Zhe Weng <wengzhe@xiaomi.com>
This commit is contained in:
Zhe Weng 2024-12-18 15:30:59 +08:00 committed by Alan C. Assis
parent 1fe07d0838
commit 84d261e3be
8 changed files with 113 additions and 151 deletions

View file

@ -40,3 +40,21 @@ config NET_IPFORWARD_NSTRUCT
WARNING: DO NOT set this setting to a value greater than or equal to
CONFIG_IOB_NBUFFERS, otherwise it may consume all the IOB and let
netdev fail to work.
config NET_IPFORWARD_ALLOC_STRUCT
int "Dynamic forwarding structures allocation"
default 0
---help---
When set to 0 all dynamic allocations are disabled
When set to 1 a new forwarding structure will be allocated every
time, and it will be free'd when no longer needed.
Setting this to 2 or more will allocate the forwarding structures
in batches (with batch size equal to this config). When a I/O buffer
chain head is no longer needed, it will be returned to the free
forwarding structures pool, and it will never be deallocated!
Note: maximum number of allocated forwarding structures is limited
to CONFIG_IOB_NBUFFERS - CONFIG_IOB_THROTTLE to avoid consuming all
the IOBs.

View file

@ -37,6 +37,7 @@
#include <nuttx/net/icmpv6.h>
#include "ipforward/ipforward.h"
#include "utils/utils.h"
#ifdef CONFIG_NET_IPFORWARD
@ -66,13 +67,12 @@
* Private Data
****************************************************************************/
/* This is an array of pre-allocating forwarding structures */
/* This is the state of the global forwarding structures */
static struct forward_s g_fwdpool[CONFIG_NET_IPFORWARD_NSTRUCT];
/* This is a list of free forwarding structures */
static FAR struct forward_s *g_fwdfree;
NET_BUFPOOL_DECLARE(g_fwdpool, sizeof(struct forward_s),
CONFIG_NET_IPFORWARD_NSTRUCT,
CONFIG_NET_IPFORWARD_ALLOC_STRUCT,
CONFIG_IOB_NBUFFERS - CONFIG_IOB_THROTTLE);
/****************************************************************************
* Public Functions
@ -91,25 +91,13 @@ static FAR struct forward_s *g_fwdfree;
void ipfwd_initialize(void)
{
FAR struct forward_s *fwd;
int i;
/* The IOB size must be such that the maximum L2 and L3 headers fit into
* the contiguous memory of the first IOB in the IOB chain.
*/
DEBUGASSERT(MAX_HDRLEN <= CONFIG_IOB_BUFSIZE);
/* Add all pre-allocated forwarding structures to the free list */
g_fwdfree = NULL;
for (i = 0; i < CONFIG_NET_IPFORWARD_NSTRUCT; i++)
{
fwd = &g_fwdpool[i];
fwd->f_flink = g_fwdfree;
g_fwdfree = fwd;
}
NET_BUFPOOL_INIT(g_fwdpool);
}
/****************************************************************************
@ -127,16 +115,7 @@ void ipfwd_initialize(void)
FAR struct forward_s *ipfwd_alloc(void)
{
FAR struct forward_s *fwd;
fwd = g_fwdfree;
if (fwd != NULL)
{
g_fwdfree = fwd->f_flink;
memset (fwd, 0, sizeof(struct forward_s));
}
return fwd;
return NET_BUFPOOL_TRYALLOC(g_fwdpool);
}
/****************************************************************************
@ -153,8 +132,7 @@ FAR struct forward_s *ipfwd_alloc(void)
void ipfwd_free(FAR struct forward_s *fwd)
{
fwd->f_flink = g_fwdfree;
g_fwdfree = fwd;
NET_BUFPOOL_FREE(g_fwdpool, fwd);
}
#endif /* CONFIG_NET_IPFORWARD */

View file

@ -259,6 +259,20 @@ config NET_TCP_NWRBCHAINS
choice for this value would be the same as the maximum number of
TCP connections.
config NET_TCP_ALLOC_WRBCHAINS
int "Dynamic I/O buffer chain heads allocation"
default 0
---help---
When set to 0 all dynamic allocations are disabled
When set to 1 a new I/O buffer chain head will be allocated every
time, and it will be free'd when no longer needed.
Setting this to 2 or more will allocate the I/O buffer chain heads
in batches (with batch size equal to this config). When a I/O buffer
chain head is no longer needed, it will be returned to the free
I/O buffer chain heads pool, and it will never be deallocated!
config NET_TCP_WRBUFFER_DEBUG
bool "Force write buffer debug"
default n

View file

@ -48,34 +48,15 @@
#if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_TCP_WRITE_BUFFERS)
/****************************************************************************
* Private Types
****************************************************************************/
/* Package all globals used by this logic into a structure */
struct wrbuffer_s
{
/* The semaphore to protect the buffers */
sem_t sem;
/* This is the list of available write buffers */
sq_queue_t freebuffers;
/* These are the pre-allocated write buffers */
struct tcp_wrbuffer_s buffers[CONFIG_NET_TCP_NWRBCHAINS];
};
/****************************************************************************
* Private Data
****************************************************************************/
/* This is the state of the global write buffer resource */
static struct wrbuffer_s g_wrbuffer;
NET_BUFPOOL_DECLARE(g_wrbuffer, sizeof(struct tcp_wrbuffer_s),
CONFIG_NET_TCP_NWRBCHAINS,
CONFIG_NET_TCP_ALLOC_WRBCHAINS, 0);
/****************************************************************************
* Public Functions
@ -94,16 +75,7 @@ static struct wrbuffer_s g_wrbuffer;
void tcp_wrbuffer_initialize(void)
{
int i;
sq_init(&g_wrbuffer.freebuffers);
nxsem_init(&g_wrbuffer.sem, 0, CONFIG_NET_TCP_NWRBCHAINS);
for (i = 0; i < CONFIG_NET_TCP_NWRBCHAINS; i++)
{
sq_addfirst(&g_wrbuffer.buffers[i].wb_node, &g_wrbuffer.freebuffers);
}
NET_BUFPOOL_INIT(g_wrbuffer);
}
/****************************************************************************
@ -127,7 +99,6 @@ void tcp_wrbuffer_initialize(void)
FAR struct tcp_wrbuffer_s *tcp_wrbuffer_timedalloc(unsigned int timeout)
{
FAR struct tcp_wrbuffer_s *wrb;
int ret;
/* We need to allocate two things: (1) A write buffer structure and (2)
* at least one I/O buffer to start the chain.
@ -137,20 +108,12 @@ FAR struct tcp_wrbuffer_s *tcp_wrbuffer_timedalloc(unsigned int timeout)
* buffer
*/
ret = net_sem_timedwait_uninterruptible(&g_wrbuffer.sem, timeout);
if (ret != OK)
wrb = NET_BUFPOOL_TIMEDALLOC(g_wrbuffer, timeout);
if (wrb == NULL)
{
return NULL;
}
/* 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 = net_iobtimedalloc(true, timeout);
@ -247,8 +210,7 @@ void tcp_wrbuffer_release(FAR struct tcp_wrbuffer_s *wrb)
/* Then free the write buffer structure */
sq_addlast(&wrb->wb_node, &g_wrbuffer.freebuffers);
nxsem_post(&g_wrbuffer.sem);
NET_BUFPOOL_FREE(g_wrbuffer, wrb);
}
/****************************************************************************
@ -304,16 +266,7 @@ uint32_t tcp_wrbuffer_inqueue_size(FAR struct tcp_conn_s *conn)
int tcp_wrbuffer_test(void)
{
int val = 0;
int ret;
ret = nxsem_get_value(&g_wrbuffer.sem, &val);
if (ret >= 0)
{
ret = val > 0 ? OK : -ENOSPC;
}
return ret;
return NET_BUFPOOL_TEST(g_wrbuffer);
}
#endif /* CONFIG_NET_TCP && CONFIG_NET_TCP_WRITE_BUFFERS */

View file

@ -103,6 +103,20 @@ config NET_UDP_NWRBCHAINS
choice for this value would be the same as the maximum number of
UDP connections.
config NET_UDP_ALLOC_WRBCHAINS
int "Dynamic I/O buffer chain heads allocation"
default 0
---help---
When set to 0 all dynamic allocations are disabled.
When set to 1 a new I/O buffer chain head will be allocated every
time, and it will be free'd when no longer needed.
Setting this to 2 or more will allocate the I/O buffer chain heads
in batches (with batch size equal to this config). When a I/O buffer
chain head is no longer needed, it will be returned to the free
I/O buffer chain heads pool, and it will never be deallocated!
config NET_UDP_WRBUFFER_DEBUG
bool "Force write buffer debug"
default n

View file

@ -47,34 +47,15 @@
#include "utils/utils.h"
#include "udp/udp.h"
/****************************************************************************
* Private Types
****************************************************************************/
/* Package all globals used by this logic into a structure */
struct wrbuffer_s
{
/* The semaphore to protect the buffers */
sem_t sem;
/* This is the list of available write buffers */
sq_queue_t freebuffers;
/* These are the pre-allocated write buffers */
struct udp_wrbuffer_s buffers[CONFIG_NET_UDP_NWRBCHAINS];
};
/****************************************************************************
* Private Data
****************************************************************************/
/* This is the state of the global write buffer resource */
static struct wrbuffer_s g_wrbuffer;
NET_BUFPOOL_DECLARE(g_wrbuffer, sizeof(struct udp_wrbuffer_s),
CONFIG_NET_UDP_NWRBCHAINS,
CONFIG_NET_UDP_ALLOC_WRBCHAINS, 0);
/****************************************************************************
* Public Functions
@ -93,16 +74,7 @@ static struct wrbuffer_s g_wrbuffer;
void udp_wrbuffer_initialize(void)
{
int i;
sq_init(&g_wrbuffer.freebuffers);
nxsem_init(&g_wrbuffer.sem, 0, CONFIG_NET_UDP_NWRBCHAINS);
for (i = 0; i < CONFIG_NET_UDP_NWRBCHAINS; i++)
{
sq_addfirst(&g_wrbuffer.buffers[i].wb_node, &g_wrbuffer.freebuffers);
}
NET_BUFPOOL_INIT(g_wrbuffer);
}
/****************************************************************************
@ -133,15 +105,8 @@ FAR struct udp_wrbuffer_s *udp_wrbuffer_alloc(void)
* buffer
*/
net_sem_wait_uninterruptible(&g_wrbuffer.sem);
/* Now, we are guaranteed to have a write buffer structure reserved
* for us in the free list.
*/
wrb = (FAR struct udp_wrbuffer_s *)sq_remfirst(&g_wrbuffer.freebuffers);
wrb = NET_BUFPOOL_ALLOC(g_wrbuffer);
DEBUGASSERT(wrb);
memset(wrb, 0, sizeof(struct udp_wrbuffer_s));
/* Now get the first I/O buffer for the write buffer structure */
@ -177,7 +142,6 @@ FAR struct udp_wrbuffer_s *udp_wrbuffer_alloc(void)
FAR struct udp_wrbuffer_s *udp_wrbuffer_timedalloc(unsigned int timeout)
{
FAR struct udp_wrbuffer_s *wrb;
int ret;
/* We need to allocate two things: (1) A write buffer structure and (2)
* at least one I/O buffer to start the chain.
@ -187,20 +151,12 @@ FAR struct udp_wrbuffer_s *udp_wrbuffer_timedalloc(unsigned int timeout)
* buffer
*/
ret = net_sem_timedwait_uninterruptible(&g_wrbuffer.sem, timeout);
if (ret != OK)
wrb = NET_BUFPOOL_TIMEDALLOC(g_wrbuffer, timeout);
if (wrb == NULL)
{
return NULL;
}
/* Now, we are guaranteed to have a write buffer structure reserved
* for us in the free list.
*/
wrb = (FAR struct udp_wrbuffer_s *)sq_remfirst(&g_wrbuffer.freebuffers);
DEBUGASSERT(wrb);
memset(wrb, 0, sizeof(struct udp_wrbuffer_s));
/* Now get the first I/O buffer for the write buffer structure */
wrb->wb_iob = net_iobtimedalloc(true, timeout);
@ -253,19 +209,12 @@ FAR struct udp_wrbuffer_s *udp_wrbuffer_tryalloc(void)
* buffer
*/
if (nxsem_trywait(&g_wrbuffer.sem) != OK)
wrb = NET_BUFPOOL_TRYALLOC(g_wrbuffer);
if (wrb == NULL)
{
return NULL;
}
/* Now, we are guaranteed to have a write buffer structure reserved
* for us in the free list.
*/
wrb = (FAR struct udp_wrbuffer_s *)sq_remfirst(&g_wrbuffer.freebuffers);
DEBUGASSERT(wrb);
memset(wrb, 0, sizeof(struct udp_wrbuffer_s));
/* Now get the first I/O buffer for the write buffer structure */
wrb->wb_iob =
@ -312,8 +261,7 @@ void udp_wrbuffer_release(FAR struct udp_wrbuffer_s *wrb)
/* Then free the write buffer structure */
sq_addlast(&wrb->wb_node, &g_wrbuffer.freebuffers);
nxsem_post(&g_wrbuffer.sem);
NET_BUFPOOL_FREE(g_wrbuffer, wrb);
}
/****************************************************************************
@ -363,9 +311,7 @@ uint32_t udp_wrbuffer_inqueue_size(FAR struct udp_conn_s *conn)
int udp_wrbuffer_test(void)
{
int val = 0;
nxsem_get_value(&g_wrbuffer.sem, &val);
return val > 0 ? OK : -ENOSPC;
return NET_BUFPOOL_TEST(g_wrbuffer);
}
#endif /* CONFIG_NET && CONFIG_NET_UDP && CONFIG_NET_UDP_WRITE_BUFFERS */

View file

@ -170,3 +170,28 @@ void net_bufpool_free(FAR struct net_bufpool_s *pool, FAR void *node)
nxsem_post(&pool->u.sem);
}
/****************************************************************************
* Name: net_bufpool_test
*
* Description:
* Check if there is room in the buffer pool. Does not reserve any space.
*
* Assumptions:
* None.
*
****************************************************************************/
int net_bufpool_test(FAR struct net_bufpool_s *pool)
{
int val = 0;
int ret;
ret = nxsem_get_value(&pool->u.sem, &val);
if (ret >= 0)
{
ret = val > 0 ? OK : -ENOSPC;
}
return ret;
}

View file

@ -102,6 +102,7 @@
#define NET_BUFPOOL_TRYALLOC(p) net_bufpool_timedalloc(&p, 0)
#define NET_BUFPOOL_ALLOC(p) net_bufpool_timedalloc(&p, UINT_MAX)
#define NET_BUFPOOL_FREE(p,n) net_bufpool_free(&p, n)
#define NET_BUFPOOL_TEST(p) net_bufpool_test(&p)
/****************************************************************************
* Public Types
@ -416,6 +417,19 @@ FAR void *net_bufpool_timedalloc(FAR struct net_bufpool_s *pool,
void net_bufpool_free(FAR struct net_bufpool_s *pool, FAR void *node);
/****************************************************************************
* Name: net_bufpool_test
*
* Description:
* Check if there is room in the buffer pool. Does not reserve any space.
*
* Assumptions:
* None.
*
****************************************************************************/
int net_bufpool_test(FAR struct net_bufpool_s *pool);
/****************************************************************************
* Name: net_chksum_adjust
*