From dd09cbc1c455ab1e067b53f46bee7b6d50689bbc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 6 Feb 2018 19:16:49 +0200 Subject: [PATCH] net: buf: Redesigned API with split data and meta-data Redesign of the net_buf_simple and net_buf structs, where the data payload portion is split to a separately allocated chunk of memory. In practice this means that buf->__buf becomes a pointer from having just been a marker (empty array) for where the payload begins right after the meta-data. Fixes #3283 Signed-off-by: Johan Hedberg --- include/net/buf.h | 273 +++++++++++++----- .../net/mqtt_publisher/prj_96b_nitrogen.conf | 1 - subsys/bluetooth/common/dummy.c | 5 + subsys/bluetooth/host/conn.c | 9 - subsys/bluetooth/host/hci_core.c | 9 - subsys/bluetooth/host/l2cap.c | 19 +- subsys/net/Kconfig | 20 +- subsys/net/buf.c | 150 ++++++++-- subsys/net/ip/Kconfig | 10 - subsys/net/ip/net_pkt.c | 7 +- 10 files changed, 357 insertions(+), 146 deletions(-) diff --git a/include/net/buf.h b/include/net/buf.h index 7ffb6d32787..dbe5e1032f2 100644 --- a/include/net/buf.h +++ b/include/net/buf.h @@ -29,28 +29,41 @@ extern "C" { /* Alignment needed for various parts of the buffer definition */ #define __net_buf_align __aligned(sizeof(int)) -/** @def NET_BUF_SIMPLE - * @brief Define a net_buf_simple stack variable and get a pointer to it. +/** @def NET_BUF_SIMPLE_DEFINE + * @brief Define a net_buf_simple stack variable. * - * This is a helper macro which is used to define a net_buf_simple object on - * the stack and the get a pointer to it as follows: - * - * struct net_buf_simple *my_buf = NET_BUF_SIMPLE(10); - * - * After creating the object it needs to be initialized by calling - * net_buf_simple_init(). + * This is a helper macro which is used to define a net_buf_simple object + * on the stack. * + * @param _name Name of the net_buf_simple object. * @param _size Maximum data storage for the buffer. - * - * @return Pointer to stack-allocated net_buf_simple object. */ -#define NET_BUF_SIMPLE(_size) \ - ((struct net_buf_simple *)(&(struct { \ - struct net_buf_simple buf; \ - u8_t data[_size] __net_buf_align; \ - }) { \ - .buf.size = _size, \ - })) +#define NET_BUF_SIMPLE_DEFINE(_name, _size) \ + u8_t net_buf_data_##_name[_size]; \ + struct net_buf_simple _name = { \ + .data = net_buf_data_##_name, \ + .len = 0, \ + .size = _size, \ + .__buf = net_buf_data_##_name, \ + } + +/** @def NET_BUF_SIMPLE_DEFINE_STATIC + * @brief Define a static net_buf_simple variable. + * + * This is a helper macro which is used to define a static net_buf_simple + * object. + * + * @param _name Name of the net_buf_simple object. + * @param _size Maximum data storage for the buffer. + */ +#define NET_BUF_SIMPLE_DEFINE_STATIC(_name, _size) \ + static u8_t net_buf_data_##_name[_size]; \ + static struct net_buf_simple _name = { \ + .data = net_buf_data_##_name, \ + .len = 0, \ + .size = _size, \ + .__buf = net_buf_data_##_name, \ + } /** @brief Simple network buffer representation. * @@ -78,24 +91,66 @@ struct net_buf_simple { /** Start of the data storage. Not to be accessed directly * (the data pointer should be used instead). */ - u8_t __buf[0] __net_buf_align; + u8_t *__buf; }; +/** @def NET_BUF_SIMPLE + * @brief Define a net_buf_simple stack variable and get a pointer to it. + * + * This is a helper macro which is used to define a net_buf_simple object on + * the stack and the get a pointer to it as follows: + * + * struct net_buf_simple *my_buf = NET_BUF_SIMPLE(10); + * + * After creating the object it needs to be initialized by calling + * net_buf_simple_init(). + * + * @param _size Maximum data storage for the buffer. + * + * @return Pointer to stack-allocated net_buf_simple object. + */ +#define NET_BUF_SIMPLE(_size) \ + ((struct net_buf_simple *)(&(struct { \ + struct net_buf_simple buf; \ + u8_t data[_size] __net_buf_align; \ + }) { \ + .buf.size = _size, \ + })) + /** @brief Initialize a net_buf_simple object. * - * This needs to be called after creating a net_buf_simple object e.g. using + * This needs to be called after creating a net_buf_simple object using * the NET_BUF_SIMPLE macro. * * @param buf Buffer to initialize. * @param reserve_head Headroom to reserve. + * + * @warning This API should *only* be used when the net_buf_simple object + * has been created using the NET_BUF_SIMPLE() macro. For any other + * kinf of creation there is no need to call this API (in fact, + * it will result in undefined behavior). */ static inline void net_buf_simple_init(struct net_buf_simple *buf, size_t reserve_head) { + buf->__buf = (u8_t *)buf + sizeof(*buf); buf->data = buf->__buf + reserve_head; buf->len = 0; } +/** + * @brief Reset buffer + * + * Reset buffer data so it can be reused for other purposes. + * + * @param buf Buffer to reset. + */ +static inline void net_buf_simple_reset(struct net_buf_simple *buf) +{ + buf->len = 0; + buf->data = buf->__buf; +} + /** * @brief Prepare data to be added at the end of the buffer * @@ -431,22 +486,30 @@ struct net_buf { /** Amount of data that this buffer can store. */ u16_t size; + + /** Start of the data storage. Not to be accessed + * directly (the data pointer should be used + * instead). + */ + u8_t *__buf; }; struct net_buf_simple b; }; - /** Start of the data storage. Not to be accessed directly - * (the data pointer should be used instead). - */ - u8_t __buf[0] __net_buf_align; + /** System metadata for this buffer. */ + u8_t user_data[CONFIG_NET_BUF_USER_DATA_SIZE] __net_buf_align; +}; - /** After __buf (as given by the "size" field, which can be 0), - * there may be so-called "user data", which is actually a system - * metadata for this buffer. This area can be accessed using - * net_buf_user_data(). (Its size is equal to - * this->pool->user_data_size.) - */ +struct net_buf_data_cb { + u8_t * (*alloc)(struct net_buf *buf, size_t *size, s32_t timeout); + u8_t * (*ref)(struct net_buf *buf, u8_t *data); + void (*unref)(struct net_buf *buf, u8_t *data); +}; + +struct net_buf_data_alloc { + const struct net_buf_data_cb *cb; + void *alloc_data; }; struct net_buf_pool { @@ -459,12 +522,6 @@ struct net_buf_pool { /** Number of uninitialized buffers */ u16_t uninit_count; - /** Data size of each buffer in the pool */ - const u16_t buf_size; - - /** Size of the user data associated with each buffer. */ - const u16_t user_data_size; - #if defined(CONFIG_NET_BUF_POOL_USAGE) /** Amount of available buffers in the pool. */ s16_t avail_count; @@ -479,39 +536,86 @@ struct net_buf_pool { /** Optional destroy callback when buffer is freed. */ void (*const destroy)(struct net_buf *buf); - /** Helper to access the start of storage (for net_buf_pool_init) */ + /** Data allocation handlers. */ + const struct net_buf_data_alloc *alloc; + + /** Start of buffer storage array */ struct net_buf * const __bufs; }; #if defined(CONFIG_NET_BUF_POOL_USAGE) -#define NET_BUF_POOL_INITIALIZER(_pool, _bufs, _count, _size, _ud_size, \ - _destroy) \ +#define NET_BUF_POOL_INITIALIZER(_pool, _alloc, _bufs, _count, _destroy) \ { \ - .free = _K_LIFO_INITIALIZER(_pool.free), \ - .__bufs = (struct net_buf *)_bufs, \ + .alloc = _alloc, \ + .free = _K_LIFO_INITIALIZER(_pool.free), \ + .__bufs = _bufs, \ .buf_count = _count, \ .uninit_count = _count, \ .avail_count = _count, \ - .pool_size = sizeof(_net_buf_##_pool), \ - .buf_size = _size, \ - .user_data_size = _ud_size, \ .destroy = _destroy, \ .name = STRINGIFY(_pool), \ } #else -#define NET_BUF_POOL_INITIALIZER(_pool, _bufs, _count, _size, _ud_size, \ - _destroy) \ +#define NET_BUF_POOL_INITIALIZER(_pool, _alloc, _bufs, _count, _destroy) \ { \ - .free = _K_LIFO_INITIALIZER(_pool.free), \ - .__bufs = (struct net_buf *)_bufs, \ + .alloc = _alloc, \ + .free = _K_LIFO_INITIALIZER(_pool.free), \ + .__bufs = _bufs, \ .buf_count = _count, \ .uninit_count = _count, \ - .buf_size = _size, \ - .user_data_size = _ud_size, \ .destroy = _destroy, \ } #endif /* CONFIG_NET_BUF_POOL_USAGE */ +struct net_buf_pool_fixed { + size_t data_size; + u8_t *data_pool; +}; + +extern const struct net_buf_data_cb net_buf_fixed_cb; + +/** @def NET_BUF_POOL_FIXED_DEFINE + * @brief Define a new pool for buffers based on fixed-size data + * + * Defines a net_buf_pool struct and the necessary memory storage (array of + * structs) for the needed amount of buffers. After this, the buffers can be + * accessed from the pool through net_buf_alloc. The pool is defined as a + * static variable, so if it needs to be exported outside the current module + * this needs to happen with the help of a separate pointer rather than an + * extern declaration. + * + * The data payload of the buffers will be allocated from a byte array + * of fixed sized chunks. This kind of pool does not support blocking on + * the data allocation, so the timeout passed to net_buf_alloc will be + * always treated as K_NO_WAIT when trying to allocate the data. This means + * that allocation failures, i.e. NULL returns, must always be handled + * cleanly. + * + * If provided with a custom destroy callback, this callback is + * responsible for eventually calling net_buf_destroy() to complete the + * process of returning the buffer to the pool. + * + * @param _name Name of the pool variable. + * @param _count Number of buffers in the pool. + * @param _data_size Maximum data payload per buffer. + * @param _destroy Optional destroy callback when buffer is freed. + */ +#define NET_BUF_POOL_FIXED_DEFINE(_name, _count, _data_size, _destroy) \ + static struct net_buf net_buf_##_name[_count] __noinit; \ + static u8_t net_buf_data_##_name[_count][_data_size]; \ + static const struct net_buf_pool_fixed net_buf_fixed_##_name = { \ + .data_size = _data_size, \ + .data_pool = (u8_t *)net_buf_data_##_name, \ + }; \ + static const struct net_buf_data_alloc net_buf_fixed_alloc_##_name = {\ + .cb = &net_buf_fixed_cb, \ + .alloc_data = (void *)&net_buf_fixed_##_name, \ + }; \ + struct net_buf_pool _name __net_buf_align \ + __in_section(_net_buf_pool, static, _name) = \ + NET_BUF_POOL_INITIALIZER(_name, &net_buf_fixed_alloc_##_name, \ + net_buf_##_name, _count, _destroy) + /** @def NET_BUF_POOL_DEFINE * @brief Define a new pool for buffers * @@ -533,16 +637,8 @@ struct net_buf_pool { * @param _destroy Optional destroy callback when buffer is freed. */ #define NET_BUF_POOL_DEFINE(_name, _count, _size, _ud_size, _destroy) \ - static struct { \ - struct net_buf buf; \ - u8_t data[_size] __net_buf_align; \ - u8_t ud[ROUND_UP(_ud_size, 4)] __net_buf_align; \ - } _net_buf_##_name[_count] __noinit; \ - struct net_buf_pool _name __net_buf_align \ - __in_section(_net_buf_pool, static, _name) = \ - NET_BUF_POOL_INITIALIZER(_name, _net_buf_##_name, \ - _count, _size, _ud_size, _destroy) - + BUILD_ASSERT(_ud_size <= CONFIG_NET_BUF_USER_DATA_SIZE); \ + NET_BUF_POOL_FIXED_DEFINE(_name, _count, _size, _destroy) /** * @brief Looks up a pool based on its ID. @@ -581,12 +677,43 @@ int net_buf_id(struct net_buf *buf); * @return New buffer or NULL if out of buffers. */ #if defined(CONFIG_NET_BUF_LOG) -struct net_buf *net_buf_alloc_debug(struct net_buf_pool *pool, s32_t timeout, - const char *func, int line); -#define net_buf_alloc(_pool, _timeout) \ - net_buf_alloc_debug(_pool, _timeout, __func__, __LINE__) +struct net_buf *net_buf_alloc_fixed_debug(struct net_buf_pool *pool, + s32_t timeout, const char *func, + int line); +#define net_buf_alloc_fixed(_pool, _timeout) \ + net_buf_alloc_fixed_debug(_pool, _timeout, __func__, __LINE__) #else -struct net_buf *net_buf_alloc(struct net_buf_pool *pool, s32_t timeout); +struct net_buf *net_buf_alloc_fixed(struct net_buf_pool *pool, s32_t timeout); +#endif + +#define net_buf_alloc(_pool, _timeout) net_buf_alloc_fixed(_pool, _timeout) + +/** + * @brief Allocate a new buffer from a pool. + * + * Allocate a new buffer from a pool. + * + * @param pool Which pool to allocate the buffer from. + * @param size Amount of data the buffer must be able to fit. + * @param timeout Affects the action taken should the 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. Note that some types + * of data allocators do not support blocking (such as the HEAP + * type). In this case it's still possible for net_buf_alloc() to + * fail (return NULL) even if it was given K_FOREVER. + * + * @return New buffer or NULL if out of buffers. + */ +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *net_buf_alloc_len_debug(struct net_buf_pool *pool, size_t size, + s32_t timeout, const char *func, + int line); +#define net_buf_alloc_len(_pool, _size, _timeout) \ + net_buf_alloc_len_debug(_pool, _size, _timeout, __func__, __LINE__) +#else +struct net_buf *net_buf_alloc_len(struct net_buf_pool *pool, size_t size, + s32_t timeout); #endif /** @@ -645,7 +772,7 @@ void net_buf_reset(struct net_buf *buf); * @param buf Buffer to initialize. * @param reserve How much headroom to reserve. */ -void net_buf_reserve(struct net_buf *buf, size_t reserve); +void net_buf_simple_reserve(struct net_buf_simple *buf, size_t reserve); /** * @brief Put a buffer into a list @@ -733,9 +860,21 @@ struct net_buf *net_buf_clone(struct net_buf *buf, s32_t timeout); */ static inline void *net_buf_user_data(struct net_buf *buf) { - return (void *)ROUND_UP((buf->__buf + buf->size), sizeof(int)); + return (void *)buf->user_data; } +/** @def net_buf_reserve + * @brief Initialize buffer with the given headroom. + * + * Initializes a buffer with a given headroom. The buffer is not expected to + * contain any data when this API is called. + * + * @param buf Buffer to initialize. + * @param reserve How much headroom to reserve. + */ +#define net_buf_reserve(buf, reserve) net_buf_simple_reserve(&(buf)->b, \ + reserve) + /** * @def net_buf_add * @brief Prepare data to be added at the end of the buffer diff --git a/samples/net/mqtt_publisher/prj_96b_nitrogen.conf b/samples/net/mqtt_publisher/prj_96b_nitrogen.conf index b5d11ddc622..bd92c967410 100644 --- a/samples/net/mqtt_publisher/prj_96b_nitrogen.conf +++ b/samples/net/mqtt_publisher/prj_96b_nitrogen.conf @@ -46,7 +46,6 @@ CONFIG_BT_DEVICE_NAME="Zephyr" CONFIG_BT_L2CAP_TX_BUF_COUNT=3 CONFIG_BT_L2CAP_TX_MTU=65 -CONFIG_BT_L2CAP_TX_USER_DATA_SIZE=4 CONFIG_BT_HCI_TX_STACK_SIZE=640 CONFIG_BT_HCI_HOST=y diff --git a/subsys/bluetooth/common/dummy.c b/subsys/bluetooth/common/dummy.c index a0d6c815509..7200c743e65 100644 --- a/subsys/bluetooth/common/dummy.c +++ b/subsys/bluetooth/common/dummy.c @@ -25,6 +25,11 @@ BUILD_ASSERT(CONFIG_SYSTEM_WORKQUEUE_PRIORITY < 0); */ BUILD_ASSERT(CONFIG_BT_HCI_TX_PRIO < CONFIG_BT_RX_PRIO); +/* The Bluetooth subsystem requires network buffers to have at least 8 bytes + * reserved for user data. + */ +BUILD_ASSERT(CONFIG_NET_BUF_USER_DATA_SIZE >= 8); + #if defined(CONFIG_BT_CTLR) /* The Bluetooth Controller's priority receive thread priority shall be higher * than the Bluetooth Host's Tx and the Controller's receive thread priority. diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index 4a5a5b2bc2f..10048ddbfdd 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -1055,17 +1055,8 @@ void bt_conn_recv(struct bt_conn *conn, struct net_buf *buf, u8_t flags) int bt_conn_send_cb(struct bt_conn *conn, struct net_buf *buf, bt_conn_tx_cb_t cb) { - struct net_buf_pool *pool; - BT_DBG("conn handle %u buf len %u cb %p", conn->handle, buf->len, cb); - pool = net_buf_pool_get(buf->pool_id); - if (pool->user_data_size < BT_BUF_USER_DATA_MIN) { - BT_ERR("Too small user data size"); - net_buf_unref(buf); - return -EINVAL; - } - if (conn->state != BT_CONN_CONNECTED) { BT_ERR("not connected!"); net_buf_unref(buf); diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 98a8834f1c7..55d490f1ec8 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -4290,19 +4290,10 @@ int bt_send(struct net_buf *buf) int bt_recv(struct net_buf *buf) { - struct net_buf_pool *pool; - bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len); BT_DBG("buf %p len %u", buf, buf->len); - pool = net_buf_pool_get(buf->pool_id); - if (pool->user_data_size < BT_BUF_USER_DATA_MIN) { - BT_ERR("Too small user data size"); - net_buf_unref(buf); - return -EINVAL; - } - switch (bt_buf_get_type(buf)) { #if defined(CONFIG_BT_CONN) case BT_BUF_ACL_IN: diff --git a/subsys/bluetooth/host/l2cap.c b/subsys/bluetooth/host/l2cap.c index ec719a1f3e6..f65969c8537 100644 --- a/subsys/bluetooth/host/l2cap.c +++ b/subsys/bluetooth/host/l2cap.c @@ -998,13 +998,10 @@ static inline struct net_buf *l2cap_alloc_seg(struct net_buf *buf) struct net_buf *seg; /* Try to use original pool if possible */ - if (pool->user_data_size >= BT_BUF_USER_DATA_MIN && - pool->buf_size >= BT_L2CAP_BUF_SIZE(L2CAP_MAX_LE_MPS)) { - seg = net_buf_alloc(pool, K_NO_WAIT); - if (seg) { - net_buf_reserve(seg, BT_L2CAP_CHAN_SEND_RESERVE); - return seg; - } + seg = net_buf_alloc(pool, K_NO_WAIT); + if (seg) { + net_buf_reserve(seg, BT_L2CAP_CHAN_SEND_RESERVE); + return seg; } /* Fallback to using global connection tx pool */ @@ -1015,7 +1012,6 @@ static struct net_buf *l2cap_chan_create_seg(struct bt_l2cap_le_chan *ch, struct net_buf *buf, size_t sdu_hdr_len) { - struct net_buf_pool *pool = net_buf_pool_get(buf->pool_id); struct net_buf *seg; u16_t headroom; u16_t len; @@ -1025,13 +1021,6 @@ static struct net_buf *l2cap_chan_create_seg(struct bt_l2cap_le_chan *ch, goto segment; } - /* Segment if there is no space in the user_data */ - if (pool->user_data_size < BT_BUF_USER_DATA_MIN) { - BT_WARN("Too small buffer user_data_size %u", - pool->user_data_size); - goto segment; - } - headroom = BT_L2CAP_CHAN_SEND_RESERVE + sdu_hdr_len; /* Check if original buffer has enough headroom and don't have any diff --git a/subsys/net/Kconfig b/subsys/net/Kconfig index f5344e3d3e1..79903cd4172 100644 --- a/subsys/net/Kconfig +++ b/subsys/net/Kconfig @@ -15,18 +15,28 @@ config NET_BUF This option enables support for generic network protocol buffers. +if NET_BUF + +config NET_BUF_USER_DATA_SIZE + int "Size of user_data available in every network buffer" + default 0 + range 8 65535 + help + Amount of memory reserved in each network buffer for user data. In + most cases this can be left as the default value. + config NET_BUF_LOG bool "Network buffer logging" - depends on NET_BUF select SYS_LOG default n help Enable logs and checks for the generic network buffers. +if NET_BUF_LOG config SYS_LOG_NET_BUF_LEVEL int prompt "Network buffer Logging level" - depends on NET_BUF_LOG && SYS_LOG + depends on SYS_LOG default 1 range 0 4 help @@ -41,7 +51,6 @@ config SYS_LOG_NET_BUF_LEVEL config NET_BUF_WARN_ALLOC_INTERVAL int prompt "Interval of Network buffer allocation warnings" - depends on NET_BUF_LOG default 1 range 0 60 help @@ -52,7 +61,6 @@ config NET_BUF_WARN_ALLOC_INTERVAL config NET_BUF_SIMPLE_LOG bool "Network buffer memory debugging" - depends on NET_BUF_LOG select SYS_LOG default n help @@ -60,7 +68,6 @@ config NET_BUF_SIMPLE_LOG config NET_BUF_POOL_USAGE bool "Network buffer pool usage tracking" - depends on NET_BUF default n help Enable network buffer pool tracking. This means that: @@ -68,6 +75,9 @@ config NET_BUF_POOL_USAGE * total size of the pool is calculated * pool name is stored and can be shown in debugging prints +endif # NET_BUF_LOG +endif # NET_BUF + config NETWORKING bool "Link layer and IP networking support" select NET_BUF diff --git a/subsys/net/buf.c b/subsys/net/buf.c index c950c2f6d70..f3699131a2f 100644 --- a/subsys/net/buf.c +++ b/subsys/net/buf.c @@ -55,22 +55,11 @@ static int pool_id(struct net_buf_pool *pool) return pool - _net_buf_pool_list; } -/* Helpers to access the storage array, since we don't have access to its - * type at this point anymore. - */ -#define BUF_SIZE(pool) (sizeof(struct net_buf) + \ - ROUND_UP(pool->buf_size, 4) + \ - ROUND_UP(pool->user_data_size, 4)) -#define UNINIT_BUF(pool, n) (struct net_buf *)(((u8_t *)(pool->__bufs)) + \ - ((n) * BUF_SIZE(pool))) - int net_buf_id(struct net_buf *buf) { struct net_buf_pool *pool = net_buf_pool_get(buf->pool_id); - u8_t *pool_start = (u8_t *)pool->__bufs; - u8_t *buf_ptr = (u8_t *)buf; - return (buf_ptr - pool_start) / BUF_SIZE(pool); + return buf - pool->__bufs; } static inline struct net_buf *pool_get_uninit(struct net_buf_pool *pool, @@ -78,10 +67,9 @@ static inline struct net_buf *pool_get_uninit(struct net_buf_pool *pool, { struct net_buf *buf; - buf = UNINIT_BUF(pool, pool->buf_count - uninit_count); + buf = &pool->__bufs[pool->buf_count - uninit_count]; buf->pool_id = pool_id(pool); - buf->size = pool->buf_size; return buf; } @@ -91,23 +79,67 @@ void net_buf_reset(struct net_buf *buf) NET_BUF_ASSERT(buf->flags == 0); NET_BUF_ASSERT(buf->frags == NULL); - buf->len = 0; - buf->data = buf->__buf; + net_buf_simple_reset(&buf->b); +} + +static u8_t *fixed_data_alloc(struct net_buf *buf, size_t *size, s32_t timeout) +{ + struct net_buf_pool *pool = net_buf_pool_get(buf->pool_id); + const struct net_buf_pool_fixed *fixed = pool->alloc->alloc_data; + + *size = min(fixed->data_size, *size); + + return fixed->data_pool + fixed->data_size * net_buf_id(buf); +} + +static void fixed_data_unref(struct net_buf *buf, u8_t *data) +{ + /* Nothing needed for fixed-size data pools */ +} + +const struct net_buf_data_cb net_buf_fixed_cb = { + .alloc = fixed_data_alloc, + .unref = fixed_data_unref, +}; + +static u8_t *data_alloc(struct net_buf *buf, size_t *size, s32_t timeout) +{ + struct net_buf_pool *pool = net_buf_pool_get(buf->pool_id); + + return pool->alloc->cb->alloc(buf, size, timeout); +} + +static u8_t *data_ref(struct net_buf *buf, u8_t *data) +{ + struct net_buf_pool *pool = net_buf_pool_get(buf->pool_id); + + return pool->alloc->cb->ref(buf, data); +} + +static void data_unref(struct net_buf *buf, u8_t *data) +{ + struct net_buf_pool *pool = net_buf_pool_get(buf->pool_id); + + pool->alloc->cb->unref(buf, data); } #if defined(CONFIG_NET_BUF_LOG) -struct net_buf *net_buf_alloc_debug(struct net_buf_pool *pool, s32_t timeout, - const char *func, int line) +struct net_buf *net_buf_alloc_len_debug(struct net_buf_pool *pool, size_t size, + s32_t timeout, const char *func, + int line) #else -struct net_buf *net_buf_alloc(struct net_buf_pool *pool, s32_t timeout) +struct net_buf *net_buf_alloc_len(struct net_buf_pool *pool, size_t size, + s32_t timeout) #endif { + u32_t alloc_start = k_uptime_get_32(); struct net_buf *buf; unsigned int key; NET_BUF_ASSERT(pool); - NET_BUF_DBG("%s():%d: pool %p timeout %d", func, line, pool, timeout); + NET_BUF_DBG("%s():%d: pool %p size %zu timeout %d", func, line, pool, + size, timeout); /* We need to lock interrupts temporarily to prevent race conditions * when accessing pool->uninit_count. @@ -178,9 +210,28 @@ struct net_buf *net_buf_alloc(struct net_buf_pool *pool, s32_t timeout) success: NET_BUF_DBG("allocated buf %p", buf); + if (size) { + if (timeout != K_NO_WAIT && timeout != K_FOREVER) { + u32_t diff = k_uptime_get_32() - alloc_start; + + timeout -= min(timeout, diff); + } + + buf->__buf = data_alloc(buf, &size, timeout); + if (!buf->__buf) { + NET_BUF_ERR("%s():%d: Failed to allocate data", + func, line); + net_buf_destroy(buf); + return NULL; + } + } else { + buf->__buf = NULL; + } + buf->ref = 1; buf->flags = 0; buf->frags = NULL; + buf->size = size; net_buf_reset(buf); #if defined(CONFIG_NET_BUF_POOL_USAGE) @@ -191,6 +242,25 @@ success: return buf; } +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *net_buf_alloc_fixed_debug(struct net_buf_pool *pool, + s32_t timeout, const char *func, + int line) +{ + const struct net_buf_pool_fixed *fixed = pool->alloc->alloc_data; + + return net_buf_alloc_len_debug(pool, fixed->data_size, timeout, func, + line); +} +#else +struct net_buf *net_buf_alloc_fixed(struct net_buf_pool *pool, s32_t timeout) +{ + const struct net_buf_pool_fixed *fixed = pool->alloc->alloc_data; + + return net_buf_alloc_len(pool, fixed->data_size, timeout); +} +#endif + #if defined(CONFIG_NET_BUF_LOG) struct net_buf *net_buf_get_debug(struct k_fifo *fifo, s32_t timeout, const char *func, int line) @@ -224,7 +294,7 @@ struct net_buf *net_buf_get(struct k_fifo *fifo, s32_t timeout) return buf; } -void net_buf_reserve(struct net_buf *buf, size_t reserve) +void net_buf_simple_reserve(struct net_buf_simple *buf, size_t reserve) { NET_BUF_ASSERT(buf); NET_BUF_ASSERT(buf->len == 0); @@ -323,6 +393,12 @@ void net_buf_unref(struct net_buf *buf) return; } + if (buf->__buf) { + data_unref(buf, buf->__buf); + buf->__buf = NULL; + } + + buf->data = NULL; buf->frags = NULL; pool = net_buf_pool_get(buf->pool_id); @@ -354,6 +430,7 @@ struct net_buf *net_buf_ref(struct net_buf *buf) struct net_buf *net_buf_clone(struct net_buf *buf, s32_t timeout) { + u32_t alloc_start = k_uptime_get_32(); struct net_buf_pool *pool; struct net_buf *clone; @@ -361,15 +438,38 @@ struct net_buf *net_buf_clone(struct net_buf *buf, s32_t timeout) pool = net_buf_pool_get(buf->pool_id); - clone = net_buf_alloc(pool, timeout); + clone = net_buf_alloc_len(pool, 0, timeout); if (!clone) { return NULL; } - net_buf_reserve(clone, net_buf_headroom(buf)); + /* If the pool supports data referencing use that. Otherwise + * we need to allocate new data and make a copy. + */ + if (pool->alloc->cb->ref) { + clone->__buf = data_ref(buf, buf->__buf); + clone->data = buf->data; + clone->len = buf->len; + clone->size = buf->size; + } else { + size_t size = buf->size; - /* TODO: Add reference to the original buffer instead of copying it. */ - memcpy(net_buf_add(clone, buf->len), buf->data, buf->len); + if (timeout != K_NO_WAIT && timeout != K_FOREVER) { + u32_t diff = k_uptime_get_32() - alloc_start; + + timeout -= min(timeout, diff); + } + + clone->__buf = data_alloc(clone, &size, timeout); + if (!clone->__buf || size < buf->size) { + net_buf_destroy(clone); + return NULL; + } + + clone->size = size; + clone->data = clone->__buf + net_buf_headroom(buf); + net_buf_add_mem(clone, buf->data, buf->len); + } return clone; } diff --git a/subsys/net/ip/Kconfig b/subsys/net/ip/Kconfig index a1349d7c321..65d63b82ad6 100644 --- a/subsys/net/ip/Kconfig +++ b/subsys/net/ip/Kconfig @@ -339,16 +339,6 @@ config NET_BUF_DATA_SIZE In order to be able to receive at least full IPv6 packet which has a size of 1280 bytes, the one should allocate 16 fragments here. -config NET_BUF_USER_DATA_SIZE - int "Size of user_data reserved" - default 0 - default 4 if NET_L2_BT - help - This is for drivers to set how much user_data shall be included in - each network data fragment. - Example: For Bluetooth, the user_data shall be at least 4 bytes as - that is used for identifying the type of data they are carrying. - choice prompt "Default Network Interface" default NET_DEFAULT_IF_FIRST diff --git a/subsys/net/ip/net_pkt.c b/subsys/net/ip/net_pkt.c index f9694ad8b74..a4be8cbf39e 100644 --- a/subsys/net/ip/net_pkt.c +++ b/subsys/net/ip/net_pkt.c @@ -272,12 +272,9 @@ void net_pkt_print_frags(struct net_pkt *pkt) frag_size = frag->size; ll_overhead = net_buf_headroom(frag); - NET_INFO("[%d] frag %p len %d size %d reserve %d " - "pool %p [sz %d ud_sz %d]", + NET_INFO("[%d] frag %p len %d size %d reserve %d pool %p", count, frag, frag->len, frag_size, ll_overhead, - net_buf_pool_get(frag->pool_id), - net_buf_pool_get(frag->pool_id)->buf_size, - net_buf_pool_get(frag->pool_id)->user_data_size); + net_buf_pool_get(frag->pool_id)); count++;