net: Add net_buf pool support to each context

User can configure its own pools for data that needs to
be transmitted out (TX). This helps to avoid deadlocking
the system if user space application uses all the buffers
in the system, and the core IP stack tries to get buffer
that needs to be sent out.

By default the net_buf pool support in net_context is turned
off as application developer needs to create the pools and
tie them to desired contexts.

Change-Id: Ida4a1771d34d6c250974e56fba4f0e0b2592cb29
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
Jukka Rissanen 2017-02-15 21:41:44 +02:00
commit 7719cee113
8 changed files with 367 additions and 44 deletions

View file

@ -313,12 +313,42 @@ static inline void net_nbuf_set_src_ipv6_addr(struct net_buf *buf)
/* @endcond */
/**
* @brief Create a TX net_buf pool that is used when sending user
* specified data to network.
*
* @param name Name of the pool.
* @param count Number of net_buf in this pool.
*/
#define NET_NBUF_TX_POOL_DEFINE(name, count) \
NET_BUF_POOL_DEFINE(name, count, 0, sizeof(struct net_nbuf), NULL)
/**
* @brief Create a DATA net_buf pool that is used when sending user
* specified data to network.
*
* @param name Name of the pool.
* @param count Number of net_buf in this pool.
*/
#define NET_NBUF_DATA_POOL_DEFINE(name, count) \
NET_BUF_POOL_DEFINE(name, count, CONFIG_NET_NBUF_DATA_SIZE, \
CONFIG_NET_NBUF_USER_DATA_SIZE, NULL)
#if defined(CONFIG_NET_DEBUG_NET_BUF)
/* Debug versions of the nbuf functions that are used when tracking
* buffer usage.
*/
struct net_buf *net_nbuf_get_reserve_debug(struct net_buf_pool *pool,
uint16_t reserve_head,
int32_t timeout,
const char *caller,
int line);
#define net_nbuf_get_reserve(pool, reserve_head, timeout) \
net_nbuf_get_reserve_debug(pool, reserve_head, timeout, \
__func__, __LINE__)
struct net_buf *net_nbuf_get_rx_debug(struct net_context *context,
int32_t timeout,
const char *caller, int line);
@ -355,6 +385,12 @@ struct net_buf *net_nbuf_get_reserve_data_debug(uint16_t reserve_head,
#define net_nbuf_get_reserve_data(res, timeout) \
net_nbuf_get_reserve_data_debug(res, timeout, __func__, __LINE__)
struct net_buf *net_nbuf_get_frag_debug(struct net_buf *buf,
int32_t timeout,
const char *caller, int line);
#define net_nbuf_get_frag(buf, timeout) \
net_nbuf_get_frag_debug(buf, timeout, __func__, __LINE__)
void net_nbuf_unref_debug(struct net_buf *buf, const char *caller, int line);
#define net_nbuf_unref(buf) net_nbuf_unref_debug(buf, __func__, __LINE__)
@ -376,6 +412,24 @@ void net_nbuf_print_frags(struct net_buf *buf);
#define net_nbuf_print_frags(...)
/**
* @brief Get buffer from the given buffer pool.
*
* @details Get network buffer from the specific buffer pool.
*
* @param pool Network buffer pool.
* @param reserve_head How many bytes to reserve for headroom.
* @param timeout Affects the action taken should the net buf pool be empty.
* If K_NO_WAIT, then return immediately. If K_FOREVER, then
* wait as long as necessary. Otherwise, wait up to the specified
* number of milliseconds before timing out.
*
* @return Network buffer if successful, NULL otherwise.
*/
struct net_buf *net_nbuf_get_reserve(struct net_buf_pool *pool,
uint16_t reserve_head,
int32_t timeout);
/**
* @brief Get buffer from the RX buffers pool.
*
@ -484,6 +538,22 @@ struct net_buf *net_nbuf_get_reserve_tx(uint16_t reserve_head,
struct net_buf *net_nbuf_get_reserve_data(uint16_t reserve_head,
int32_t timeout);
/**
* @brief Get a data fragment that might be from user specific
* buffer pool or from global DATA pool.
*
* @param buf Network buffer. This must be the first buffer of the
* buffer chain with user data part in it.
* @param timeout Affects the action taken should the net buf pool be empty.
* If K_NO_WAIT, then return immediately. If K_FOREVER, then
* wait as long as necessary. Otherwise, wait up to the specified
* number of milliseconds before timing out.
*
* @return Network buffer if successful, NULL otherwise.
*/
struct net_buf *net_nbuf_get_frag(struct net_buf *buf,
int32_t timeout);
/**
* @brief Place buffer back into the available buffers pool.
*

View file

@ -139,6 +139,19 @@ typedef void (*net_context_connect_cb_t)(struct net_context *context,
int status,
void *user_data);
/* The net_nbuf_get_pool_func_t is here in order to avoid circular
* dependency between nbuf.h and net_context.h
*/
/**
* @typedef net_nbuf_get_pool_func_t
*
* @brief Function that is called to get the pool that is used
* for net_buf allocations.
*
* @return Pointer to valid struct net_buf_pool instance.
*/
typedef struct net_buf_pool *(*net_nbuf_get_pool_func_t)(void);
struct net_tcp;
struct net_conn_handle;
@ -184,6 +197,16 @@ struct net_context {
*/
void *user_data;
#if defined(CONFIG_NET_CONTEXT_NBUF_POOL)
/** Get TX net_buf pool for this context.
*/
net_nbuf_get_pool_func_t tx_pool;
/** Get DATA net_buf pool for this context.
*/
net_nbuf_get_pool_func_t data_pool;
#endif /* CONFIG_NET_CONTEXT_NBUF_POOL */
#if defined(CONFIG_NET_CONTEXT_SYNC_RECV)
/**
* Semaphore to signal synchronous recv call completion.
@ -697,6 +720,35 @@ typedef void (*net_context_cb_t)(struct net_context *context, void *user_data);
*/
void net_context_foreach(net_context_cb_t cb, void *user_data);
/**
* @brief Create network buffer pool that is used by the IP stack
* to allocate network buffers that are used by the context when
* sending data to network.
*
* @param context Context that will use the given net_buf pools.
* @param tx_pool Pointer to the function that will return TX pool
* to the caller. The TX pool is used when sending data to network.
* There is one TX net_buf for each network packet that is sent.
* @param data_pool Pointer to the function that will return DATA pool
* to the caller. The DATA pool is used to store data that is sent to
* the network.
*/
#if defined(CONFIG_NET_CONTEXT_NBUF_POOL)
static inline void net_context_setup_pools(struct net_context *context,
net_nbuf_get_pool_func_t tx_pool,
net_nbuf_get_pool_func_t data_pool)
{
NET_ASSERT(context);
NET_ASSERT(tx_pool);
NET_ASSERT(data_pool);
context->tx_pool = tx_pool;
context->data_pool = data_pool;
}
#else
#define net_context_setup_pools(context, tx_pool, data_pool)
#endif
#ifdef __cplusplus
}
#endif

View file

@ -149,6 +149,17 @@ config NET_MAX_CONTEXTS
is used when listening or sending network traffic. This is very
similar as one could call a network socket in some other systems.
config NET_CONTEXT_NBUF_POOL
bool "Enable net_buf TX pool / context"
default n
help
If enabled, then it is possible to fine-tune network buffer pool
for each context when sending network data. If this setting is
enabled, then you should define the context pools in your application
using NET_NBUF_TX_POOL_DEFINE() and NET_NBUF_DATA_POOL_DEFINE()
macros and tie these pools to desired context using the
net_context_setup_pools() function.
config NET_CONTEXT_SYNC_RECV
bool "Support synchronous functionality in net_context_recv() API"
default y

View file

@ -30,7 +30,7 @@ struct net_buf *net_ipv4_create_raw(struct net_buf *buf,
{
struct net_buf *header;
header = net_nbuf_get_reserve_data(reserve, K_FOREVER);
header = net_nbuf_get_frag(buf, K_FOREVER);
net_buf_frag_insert(buf, header);

View file

@ -322,7 +322,7 @@ struct net_buf *net_ipv6_create_raw(struct net_buf *buf,
{
struct net_buf *header;
header = net_nbuf_get_reserve_data(reserve, K_FOREVER);
header = net_nbuf_get_frag(buf, K_FOREVER);
net_buf_frag_insert(buf, header);
@ -550,7 +550,7 @@ static struct net_buf *update_ll_reserve(struct net_buf *buf,
while (orig_frag) {
if (!room_len) {
frag = net_nbuf_get_reserve_data(reserve, K_FOREVER);
frag = net_nbuf_get_frag(buf, K_FOREVER);
net_buf_frag_add(buf, frag);

View file

@ -116,9 +116,46 @@ static inline int16_t get_frees(struct net_buf_pool *pool)
}
#endif
static inline bool is_data_pool(struct net_buf_pool *pool)
{
/* The user data can only be found in TX/RX pool and it
* is always the size of struct net_nbuf.
*/
if (pool->user_data_size != sizeof(struct net_nbuf)) {
return true;
}
return false;
}
static inline bool is_from_data_pool(struct net_buf *buf)
{
return (buf->pool == &data_buffers);
return is_data_pool(buf->pool);
}
#if defined(CONFIG_NET_CONTEXT_NBUF_POOL)
static inline struct net_buf_pool *get_tx_pool(struct net_context *context)
{
return context->tx_pool();
}
static inline struct net_buf_pool *get_data_pool(struct net_context *context)
{
return context->data_pool();
}
#else
#define get_tx_pool(context) NULL
#define get_data_pool(context) NULL
#endif /* CONFIG_NET_CONTEXT_NBUF_POOL */
static inline bool is_external_pool(struct net_buf_pool *pool)
{
if (pool != &rx_buffers && pool != &tx_buffers &&
pool != &data_buffers) {
return true;
}
return false;
}
#if defined(CONFIG_NET_DEBUG_NET_BUF)
@ -130,6 +167,8 @@ static inline const char *pool2str(struct net_buf_pool *pool)
return "TX";
} else if (pool == &data_buffers) {
return "DATA";
} else if (is_data_pool(pool)) {
return "EDATA";
}
return "EXTERNAL";
@ -167,15 +206,15 @@ void net_nbuf_print_frags(struct net_buf *buf)
count * ll_overhead, (total * 100) / (count * frag_size));
}
static struct net_buf *net_nbuf_get_reserve_debug(struct net_buf_pool *pool,
uint16_t reserve_head,
int32_t timeout,
const char *caller,
int line)
struct net_buf *net_nbuf_get_reserve_debug(struct net_buf_pool *pool,
uint16_t reserve_head,
int32_t timeout,
const char *caller,
int line)
#else /* CONFIG_NET_DEBUG_NET_BUF */
static struct net_buf *net_nbuf_get_reserve(struct net_buf_pool *pool,
uint16_t reserve_head,
int32_t timeout)
struct net_buf *net_nbuf_get_reserve(struct net_buf_pool *pool,
uint16_t reserve_head,
int32_t timeout)
#endif /* CONFIG_NET_DEBUG_NET_BUF */
{
struct net_buf *buf = NULL;
@ -195,7 +234,7 @@ static struct net_buf *net_nbuf_get_reserve(struct net_buf_pool *pool,
return NULL;
}
if (pool == &data_buffers) {
if (is_data_pool(pool)) {
/* The buf->data will point to the start of the L3
* header (like IPv4 or IPv6 packet header).
*/
@ -206,13 +245,60 @@ static struct net_buf *net_nbuf_get_reserve(struct net_buf_pool *pool,
NET_BUF_CHECK_IF_NOT_IN_USE(buf, buf->ref + 1);
NET_DBG("%s [%d] buf %p reserve %u ref %d (%s():%d)",
pool2str(pool), get_frees(pool),
#if defined(CONFIG_NET_DEBUG_NET_BUF)
NET_DBG("%s (%p) [%d] buf %p reserve %u ref %d (%s():%d)",
pool2str(pool), pool, get_frees(pool),
buf, reserve_head, buf->ref, caller, line);
#endif
return buf;
}
/* Get a fragment, try to figure out the pool from where to get
* the data.
*/
#if defined(CONFIG_NET_DEBUG_NET_BUF)
struct net_buf *net_nbuf_get_frag_debug(struct net_buf *buf,
int32_t timeout,
const char *caller, int line)
#else
struct net_buf *net_nbuf_get_frag(struct net_buf *buf,
int32_t timeout)
#endif
{
#if defined(CONFIG_NET_CONTEXT_NBUF_POOL)
struct net_context *context;
#endif
if (is_from_data_pool(buf)) {
/* We cannot know the correct data pool in this case (because
* we do not know the context). Return error to the caller.
*/
return NULL;
}
#if defined(CONFIG_NET_CONTEXT_NBUF_POOL)
context = net_nbuf_context(buf);
if (context && context->data_pool) {
#if defined(CONFIG_NET_DEBUG_NET_BUF)
return net_nbuf_get_reserve_debug(context->data_pool(),
net_nbuf_ll_reserve(buf),
timeout, caller, line);
#else
return net_nbuf_get_reserve(context->data_pool(),
net_nbuf_ll_reserve(buf), timeout);
#endif /* CONFIG_NET_DEBUG_NET_BUF */
}
#endif /* CONFIG_NET_CONTEXT_NBUF_POOL */
#if defined(CONFIG_NET_DEBUG_NET_BUF)
return net_nbuf_get_reserve_data_debug(net_nbuf_ll_reserve(buf),
timeout, caller, line);
#else
return net_nbuf_get_reserve_data(net_nbuf_ll_reserve(buf), timeout);
#endif
}
#if defined(CONFIG_NET_DEBUG_NET_BUF)
struct net_buf *net_nbuf_get_reserve_rx_debug(uint16_t reserve_head,
int32_t timeout,
@ -300,7 +386,7 @@ static struct net_buf *net_nbuf_get(struct net_buf_pool *pool,
return buf;
}
if (pool != &data_buffers) {
if (!is_data_pool(pool)) {
net_nbuf_set_context(buf, context);
net_nbuf_set_ll_reserve(buf, reserve);
net_nbuf_set_iface(buf, iface);
@ -326,15 +412,20 @@ struct net_buf *net_nbuf_get_tx_debug(struct net_context *context,
int32_t timeout,
const char *caller, int line)
{
return net_nbuf_get_debug(&tx_buffers, context, timeout, caller, line);
struct net_buf_pool *pool = get_tx_pool(context);
return net_nbuf_get_debug(pool ? pool : &tx_buffers, context,
timeout, caller, line);
}
struct net_buf *net_nbuf_get_data_debug(struct net_context *context,
int32_t timeout,
const char *caller, int line)
{
return net_nbuf_get_debug(&data_buffers, context, timeout,
caller, line);
struct net_buf_pool *pool = get_data_pool(context);
return net_nbuf_get_debug(pool ? pool : &data_buffers, context,
timeout, caller, line);
}
#else /* CONFIG_NET_DEBUG_NET_BUF */
@ -348,16 +439,24 @@ struct net_buf *net_nbuf_get_rx(struct net_context *context, int32_t timeout)
struct net_buf *net_nbuf_get_tx(struct net_context *context, int32_t timeout)
{
struct net_buf_pool *pool;
NET_ASSERT_INFO(context, "TX context not set");
return net_nbuf_get(&tx_buffers, context, timeout);
pool = get_tx_pool(context);
return net_nbuf_get(pool ? pool : &tx_buffers, context, timeout);
}
struct net_buf *net_nbuf_get_data(struct net_context *context, int32_t timeout)
{
struct net_buf_pool *pool;
NET_ASSERT_INFO(context, "Data context not set");
return net_nbuf_get(&data_buffers, context, timeout);
pool = get_data_pool(context);
return net_nbuf_get(pool ? pool : &data_buffers, context, timeout);
}
#endif /* CONFIG_NET_DEBUG_NET_BUF */
@ -383,11 +482,11 @@ void net_nbuf_unref(struct net_buf *buf)
return;
}
NET_DBG("%s [%d] buf %p ref %d frags %p (%s():%d)",
pool2str(buf->pool), get_frees(buf->pool),
#if defined(CONFIG_NET_DEBUG_NET_BUF)
NET_DBG("%s (%p) [%d] buf %p ref %d frags %p (%s():%d)",
pool2str(buf->pool), buf->pool, get_frees(buf->pool),
buf, buf->ref - 1, buf->frags, caller, line);
#if defined(CONFIG_NET_DEBUG_NET_BUF)
if (buf->ref > 1) {
goto done;
}
@ -397,9 +496,10 @@ void net_nbuf_unref(struct net_buf *buf)
*/
frag = buf->frags;
while (frag) {
NET_DBG("%s [%d] buf %p ref %d frags %p (%s():%d)",
pool2str(frag->pool), get_frees(frag->pool),
frag, frag->ref - 1, frag->frags, caller, line);
NET_DBG("%s (%p) [%d] buf %p ref %d frags %p (%s():%d)",
pool2str(frag->pool), frag->pool,
get_frees(frag->pool), frag, frag->ref - 1,
frag->frags, caller, line);
if (!frag->ref) {
NET_DBG("*** ERROR *** frag %p is freed already "
@ -427,9 +527,11 @@ struct net_buf *net_nbuf_ref(struct net_buf *buf)
return NULL;
}
NET_DBG("%s [%d] buf %p ref %d (%s():%d)",
pool2str(buf->pool), get_frees(buf->pool),
#if defined(CONFIG_NET_DEBUG_NET_BUF)
NET_DBG("%s (%p) [%d] buf %p ref %d (%s():%d)",
pool2str(buf->pool), buf->pool, get_frees(buf->pool),
buf, buf->ref + 1, caller, line);
#endif
return net_buf_ref(buf);
}
@ -744,7 +846,6 @@ static inline bool net_nbuf_append_bytes(struct net_buf *buf,
uint16_t len, int32_t timeout)
{
struct net_buf *frag = net_buf_frag_last(buf);
uint16_t ll_reserve = net_nbuf_ll_reserve(buf);
do {
uint16_t count = min(len, net_buf_tailroom(frag));
@ -758,7 +859,7 @@ static inline bool net_nbuf_append_bytes(struct net_buf *buf,
return true;
}
frag = net_nbuf_get_reserve_data(ll_reserve, timeout);
frag = net_nbuf_get_frag(buf, timeout);
if (!frag) {
return false;
}
@ -788,8 +889,7 @@ bool net_nbuf_append(struct net_buf *buf, uint16_t len, const uint8_t *data,
}
if (!buf->frags) {
frag = net_nbuf_get_reserve_data(net_nbuf_ll_reserve(buf),
timeout);
frag = net_nbuf_get_frag(buf, timeout);
if (!frag) {
return false;
}
@ -923,8 +1023,7 @@ static inline struct net_buf *check_and_create_data(struct net_buf *buf,
return data;
}
frag = net_nbuf_get_reserve_data(net_nbuf_ll_reserve(buf),
timeout);
frag = net_nbuf_get_frag(buf, timeout);
if (!frag) {
return NULL;
}
@ -1025,15 +1124,11 @@ struct net_buf *net_nbuf_write(struct net_buf *buf, struct net_buf *frag,
uint16_t len, uint8_t *data,
int32_t timeout)
{
uint16_t ll_reserve;
if (!buf || is_from_data_pool(buf)) {
NET_ERR("Invalid buffer or it is data fragment");
goto error;
}
ll_reserve = net_nbuf_ll_reserve(buf);
frag = adjust_write_offset(buf, frag, offset, &offset, timeout);
if (!frag) {
NET_DBG("Failed to adjust offset");
@ -1067,7 +1162,7 @@ struct net_buf *net_nbuf_write(struct net_buf *buf, struct net_buf *frag,
frag = frag->frags;
if (!frag) {
frag = net_nbuf_get_reserve_data(ll_reserve, timeout);
frag = net_nbuf_get_frag(buf, timeout);
if (!frag) {
goto error;
}
@ -1117,8 +1212,7 @@ static inline bool insert_data(struct net_buf *buf, struct net_buf *frag,
data += count;
offset = 0;
insert = net_nbuf_get_reserve_data(net_nbuf_ll_reserve(buf),
timeout);
insert = net_nbuf_get_frag(buf, timeout);
if (!insert) {
return false;
}
@ -1190,8 +1284,7 @@ bool net_nbuf_insert(struct net_buf *buf, struct net_buf *frag,
*/
bytes = frag->len - offset;
if (bytes) {
temp = net_nbuf_get_reserve_data(net_nbuf_ll_reserve(buf),
timeout);
temp = net_nbuf_get_frag(buf, timeout);
if (!temp) {
return false;
}

View file

@ -1189,6 +1189,17 @@ static void buf_get_sockaddr(sa_family_t family, struct net_buf *buf,
#endif
}
#if defined(CONFIG_NET_CONTEXT_NBUF_POOL)
static inline void copy_pool_vars(struct net_context *new_context,
struct net_context *listen_context)
{
new_context->tx_pool = listen_context->tx_pool;
new_context->data_pool = listen_context->data_pool;
}
#else
#define copy_pool_vars(...)
#endif /* CONFIG_NET_CONTEXT_NBUF_POOL */
/* This callback is called when we are waiting connections and we receive
* a packet. We need to check if we are receiving proper msg (SYN) here.
* The ACK could also be received, in which case we have an established
@ -1394,6 +1405,7 @@ NET_CONN_CB(tcp_syn_rcvd)
tmp_tcp->accept_cb = tcp->accept_cb;
tcp->accept_cb = NULL;
new_context->tcp = tcp;
copy_pool_vars(new_context, context);
context->tcp = tmp_tcp;
tcp->context = new_context;

View file

@ -551,6 +551,80 @@ static int shell_cmd_iface(int argc, char *argv[])
return 0;
}
struct ctx_info {
int pos;
bool are_external_pools;
struct net_buf_pool *tx_pools[CONFIG_NET_MAX_CONTEXTS];
struct net_buf_pool *data_pools[CONFIG_NET_MAX_CONTEXTS];
};
#if defined(CONFIG_NET_CONTEXT_NBUF_POOL)
static bool pool_found_already(struct ctx_info *info,
struct net_buf_pool *pool)
{
int i;
for (i = 0; i < CONFIG_NET_MAX_CONTEXTS; i++) {
if (info->tx_pools[i] == pool ||
info->data_pools[i] == pool) {
return true;
}
}
return false;
}
#endif
static void context_info(struct net_context *context, void *user_data)
{
#if defined(CONFIG_NET_CONTEXT_NBUF_POOL)
struct ctx_info *info = user_data;
struct net_buf_pool *pool;
if (!net_context_is_used(context)) {
return;
}
if (context->tx_pool) {
pool = context->tx_pool();
if (pool_found_already(info, pool)) {
return;
}
#if defined(CONFIG_NET_DEBUG_NET_BUF)
printk("\tETX\t%d bytes, %d elements, available %d (%p)\n",
pool->pool_size, pool->buf_count, pool->avail_count,
pool);
#else
printk("\tETX\t%d elements (%p)\n", pool->buf_count, pool);
#endif
info->are_external_pools = true;
info->tx_pools[info->pos] = pool;
}
if (context->data_pool) {
pool = context->data_pool();
if (pool_found_already(info, pool)) {
return;
}
#if defined(CONFIG_NET_DEBUG_NET_BUF)
printk("\tEDATA\t%d bytes, %d elements, available %d (%p)\n",
pool->pool_size, pool->buf_count, pool->avail_count,
pool);
#else
printk("\tEDATA\t%d elements (%p)\n", pool->buf_count, pool);
#endif
info->are_external_pools = true;
info->data_pools[info->pos] = pool;
}
info->pos++;
#endif /* CONFIG_NET_CONTEXT_NBUF_POOL */
}
static int shell_cmd_mem(int argc, char *argv[])
{
size_t tx_size, rx_size, data_size;
@ -586,6 +660,17 @@ static int shell_cmd_mem(int argc, char *argv[])
}
printk("\n");
if (IS_ENABLED(CONFIG_NET_CONTEXT_NBUF_POOL)) {
struct ctx_info info;
memset(&info, 0, sizeof(info));
net_context_foreach(context_info, &info);
if (!info.are_external_pools) {
printk("No external memory pools found.\n");
}
}
return 0;
}