net: Add initial generic buffer implementation

We need to have a generic buffer API in order to efficiently transfer
data between different subsystems. The first such case will be the
Networking and Bluetooth subsystems where 6LoWPAN data will be passed
back and forth.

The needed API needs to provide enough flexibility for different
buffer sizes as well as custom protocol-specific context data.

The implementation offered in this patch follows the general design of
the existing Networking and Bluetooth buffer implementations by using
a backing array of buffer which is fed into a "free buffers" FIFO for
management. The main difference is that the API allows specifying
variable sized buffers for each created pool, as well as a minimum
amount of "user data" that's allocated as part of each buffer.

There's also an optional destroy callback that's e.g. useful for HCI
flow control in Bluetooth (for notifying the controller of available
buffers).

Change-Id: I00b7007135a0ff35219f38f48658f31728fbb7ca
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
Johan Hedberg 2015-10-07 18:32:54 +03:00 committed by Anas Nashif
commit c1f007687f
11 changed files with 545 additions and 0 deletions

249
include/net/buf.h Normal file
View file

@ -0,0 +1,249 @@
/** @file
* @brief Buffer management.
*/
/*
* Copyright (c) 2015 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __NET_BUF_H
#define __NET_BUF_H
#include <stddef.h>
#include <stdint.h>
#include <toolchain.h>
#include <misc/util.h>
#include <nanokernel.h>
/* Alignment needed for various parts of the buffer definition */
#define __net_buf_align __aligned(sizeof(int))
struct net_buf {
/** FIFO uses first 4 bytes itself, reserve space */
int __unused;
/** Size of the user data associated with this buffer. */
const uint16_t user_data_size;
/** Reference count. */
uint8_t ref;
/** Pointer to the start of data in the buffer. */
uint8_t *data;
/** Length of the data behind the data pointer. */
uint16_t len;
/** Amount of data that this buffer can store. */
const uint16_t size;
/** Where the buffer should go when freed up. */
struct nano_fifo * const free;
/** Function to be called when the buffer is freed. */
void (*const destroy)(struct net_buf *buf);
/** Start of the data storage. Not to be accessed directly
* (the data pointer should be used instead).
*/
uint8_t __buf[0] __net_buf_align;
};
/** @brief Define a pool of buffers of a certain amount and size.
*
* Defines the necessary memory space (array of structs) for the needed
* amount of buffers. After this the net_buf_pool_init() API still
* needs to be used (at runtime), after which the buffers can be
* accessed using the fifo given as one of the parameters.
*
* @param _name Name of buffer pool.
* @param _count Number of buffers in the pool.
* @param _size Maximum data size for each buffer.
* @param _fifo FIFO for the buffers when they are unused.
* @param _destroy Optional destroy callback when buffer is freed.
* @param _ud_size Amount of user data space to reserve.
*/
#define NET_BUF_POOL(_name, _count, _size, _fifo, _destroy, _ud_size) \
struct { \
struct net_buf buf; \
uint8_t data[ROUND_UP(_size, 4)] __net_buf_align; \
uint8_t ud[ROUND_UP(_ud_size, 4)] __net_buf_align; \
} _name[_count] = { \
[0 ... (_count - 1)] = { .buf = { \
.user_data_size = ROUND_UP(_ud_size, 4), \
.free = _fifo, \
.destroy = _destroy, \
.size = ROUND_UP(_size, 4) } }, \
}
/** @brief Initialize an available buffers FIFO based on a pool.
*
* Initializes a buffer pool created using NET_BUF_POOL(). After
* calling this API the buffers can ge accessed through the FIFO that
* was given to NET_BUF_POOL(), i.e. after this call there should be no
* need to access the buffer pool (struct array) directly anymore.
*
* @param pool Buffer pool to initialize.
*/
#define net_buf_pool_init(pool) \
do { \
int i; \
\
nano_fifo_init(pool[0].buf.free); \
\
for (i = 0; i < ARRAY_SIZE(pool); i++) { \
nano_fifo_put(pool[i].buf.free, &pool[i]); \
} \
} while (0)
/** @brief Get a new buffer from the pool.
*
* Get buffer from the available buffers pool with specified type and
* reserved headroom.
*
* @param fifo Which FIFO to take the buffer from.
* @param reserve_head How much headroom to reserve.
*
* @return New buffer or NULL if out of buffers.
*
* @warning If there are no available buffers and the function is
* called from a task or fiber the call will block until a buffer
* becomes available in the pool.
*/
struct net_buf *net_buf_get(struct nano_fifo *fifo, size_t reserve_head);
/** @brief Decrements the reference count of a buffer.
*
* Decrements the reference count of a buffer and puts it back into the
* pool if the count reaches zero.
*
* @param buf Buffer.
*/
void net_buf_unref(struct net_buf *buf);
/** Increment the reference count of a buffer.
*
* Increment the reference count of a buffer.
*
* @param buf Buffer.
*/
struct net_buf *net_buf_ref(struct net_buf *buf);
/** @brief Duplicate buffer
*
* Duplicate given buffer including any data and headers currently stored.
*
* @param buf Buffer.
*
* @return Duplicated buffer or NULL if out of buffers.
*/
struct net_buf *net_buf_clone(struct net_buf *buf);
/** Get a pointer to the user data of a buffer.
*
* @param buf The buffer in question.
*
* @return Pointer to the user data of the buffer.
*/
#define net_buf_user_data(buf) \
((void *)(ROUND_UP(((buf)->__buf + (buf)->size), sizeof(int))))
/** @brief Prepare data to be added at the end of the buffer
*
* Increments the data length of a buffer to account for more data
* at the end.
*
* @param buf Buffer to update.
* @param len Number of bytes to increment the length with.
*
* @return The original tail of the buffer.
*/
void *net_buf_add(struct net_buf *buf, size_t len);
/** @brief Add 16-bit value at the end of the buffer
*
* Adds 16-bit value in little endian format at the end of buffer.
* Increments the data length of a buffer to account for more data
* at the end.
*
* @param buf Buffer to update.
* @param value 16-bit value to be added.
*
* @return void
*/
void net_buf_add_le16(struct net_buf *buf, uint16_t value);
/** @brief Push data to the beginning of the buffer.
*
* Modifies the data pointer and buffer length to account for more data
* in the beginning of the buffer.
*
* @param buf Buffer to update.
* @param len Number of bytes to add to the beginning.
*
* @return The new beginning of the buffer data.
*/
void *net_buf_push(struct net_buf *buf, size_t len);
/** @brief Remove data from the beginning of the buffer.
*
* Removes data from the beginnig of the buffer by modifying the data
* pointer and buffer length.
*
* @param buf Buffer to update.
* @param len Number of bytes to remove.
*
* @return New beginning of the buffer data.
*/
void *net_buf_pull(struct net_buf *buf, size_t len);
/** @brief Remove and convert 16 bits from the beginning of the buffer.
*
* Same idea as with bt_buf_pull(), but a helper for operating on
* 16-bit little endian data.
*
* @param buf Buffer.
*
* @return 16-bit value converted from little endian to host endian.
*/
uint16_t net_buf_pull_le16(struct net_buf *buf);
/** @brief Check buffer tailroom.
*
* Check how much free space there is at the end of the buffer.
*
* @return Number of bytes available at the end of the buffer.
*/
size_t net_buf_tailroom(struct net_buf *buf);
/** @brief Check buffer headroom.
*
* Check how much free space there is in the beginning of the buffer.
*
* @return Number of bytes available in the beginning of the buffer.
*/
size_t net_buf_headroom(struct net_buf *buf);
/** @def net_buf_tail
* @brief Get the tail pointer for a buffer.
*
* Get a pointer to the end of the data in a buffer.
*
* @param buf Buffer.
*
* @return Tail pointer for the buffer.
*/
#define net_buf_tail(buf) ((buf)->data + (buf)->len)
#endif /* __NET_BUF_H */

View file

@ -22,4 +22,18 @@ source "net/bluetooth/Kconfig"
source "net/ip/Kconfig"
config NET_BUF
bool "Network buffer support"
default n
help
This option enebles support for generic network protocol
buffers.
config NET_BUF_DEBUG
bool "Network buffer debugging"
depends on NET_BUF
default n
help
Enable debug logs and checks for the generic network buffers.
endmenu

View file

@ -1,2 +1,3 @@
obj-$(CONFIG_BLUETOOTH) += bluetooth/
obj-$(CONFIG_NETWORKING) += ip/
obj-$(CONFIG_NET_BUF) += buf.o

View file

@ -21,6 +21,7 @@ menuconfig BLUETOOTH
prompt "Bluetooth LE support"
default n
select NANO_TIMEOUTS
select NET_BUF
help
This option enables Bluetooth Low Energy support.

169
net/buf.c Normal file
View file

@ -0,0 +1,169 @@
/* buf.c - Buffer management */
/*
* Copyright (c) 2015 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <nanokernel.h>
#include <toolchain.h>
#include <stdio.h>
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <misc/byteorder.h>
#include <net/buf.h>
#if defined(CONFIG_NET_BUF_DEBUG)
#define NET_BUF_DBG(fmt, ...) printf("buf: %s (%p): " fmt, __func__, \
sys_thread_self_get(), ##__VA_ARGS__)
#define NET_BUF_ERR(fmt, ...) printf("buf: %s: " fmt, __func__, ##__VA_ARGS__)
#define NET_BUF_WARN(fmt, ...) printf("buf: %s: " fmt, __func__, ##__VA_ARGS__)
#define NET_BUF_INFO(fmt, ...) printf("buf: " fmt, ##__VA_ARGS__)
#define NET_BUF_ASSERT(cond) (if (!(cond)) { \
NET_BUF_ERR("buf: assert: '" #cond "' failed\n"); \
})
#else
#define NET_BUF_DBG(fmt, ...)
#define NET_BUF_ERR(fmt, ...)
#define NET_BUF_WARN(fmt, ...)
#define NET_BUF_INFO(fmt, ...)
#define NET_BUF_ASSERT(cond)
#endif /* CONFIG_net_buf_DEBUG */
struct net_buf *net_buf_get(struct nano_fifo *fifo, size_t reserve_head)
{
struct net_buf *buf;
NET_BUF_DBG("fifo %p reserve %u\n", fifo, reserve_head);
buf = nano_fifo_get(fifo);
if (!buf) {
if (sys_execution_context_type_get() == NANO_CTX_ISR) {
NET_BUF_ERR("Failed to get free buffer\n");
return NULL;
}
NET_BUF_WARN("Low on buffers. Waiting (fifo %p)\n", fifo);
buf = nano_fifo_get_wait(fifo);
}
buf->ref = 1;
buf->data = buf->__buf + reserve_head;
buf->len = 0;
NET_BUF_DBG("buf %p fifo %p reserve %u\n", buf, fifo, reserve_head);
return buf;
}
void net_buf_unref(struct net_buf *buf)
{
NET_BUF_DBG("buf %p ref %u fifo %p\n", buf, buf->ref, buf->free);
NET_BUF_ASSERT(buf->ref > 0);
if (--buf->ref) {
return;
}
if (buf->destroy) {
buf->destroy(buf);
}
nano_fifo_put(buf->free, buf);
}
struct net_buf *net_buf_ref(struct net_buf *buf)
{
NET_BUF_DBG("buf %p (old) ref %u fifo %p\n", buf, buf->ref, buf->free);
buf->ref++;
return buf;
}
struct net_buf *net_buf_clone(struct net_buf *buf)
{
struct net_buf *clone;
clone = net_buf_get(buf->free, net_buf_headroom(buf));
if (!clone) {
return NULL;
}
/* TODO: Add reference to the original buffer instead of copying it. */
memcpy(net_buf_add(clone, buf->len), buf->data, buf->len);
return clone;
}
void *net_buf_add(struct net_buf *buf, size_t len)
{
uint8_t *tail = net_buf_tail(buf);
NET_BUF_DBG("buf %p len %u\n", buf, len);
NET_BUF_ASSERT(net_buf_tailroom(buf) >= len);
buf->len += len;
return tail;
}
void net_buf_add_le16(struct net_buf *buf, uint16_t value)
{
NET_BUF_DBG("buf %p value %u\n", buf, value);
value = sys_cpu_to_le16(value);
memcpy(net_buf_add(buf, sizeof(value)), &value, sizeof(value));
}
void *net_buf_push(struct net_buf *buf, size_t len)
{
NET_BUF_DBG("buf %p len %u\n", buf, len);
NET_BUF_ASSERT(net_buf_headroom(buf) >= len);
buf->data -= len;
buf->len += len;
return buf->data;
}
void *net_buf_pull(struct net_buf *buf, size_t len)
{
NET_BUF_DBG("buf %p len %u\n", buf, len);
NET_BUF_ASSERT(buf->len >= len);
buf->len -= len;
return buf->data += len;
}
uint16_t net_buf_pull_le16(struct net_buf *buf)
{
uint16_t value;
value = UNALIGNED_GET((uint16_t *)buf->data);
net_buf_pull(buf, sizeof(value));
return sys_le16_to_cpu(value);
}
size_t net_buf_headroom(struct net_buf *buf)
{
return buf->data - buf->__buf;
}
size_t net_buf_tailroom(struct net_buf *buf)
{
return buf->size - net_buf_headroom(buf) - buf->len;
}

View file

@ -0,0 +1,6 @@
PLATFORM_CONFIG ?= basic_atom
MDEF_FILE = prj.mdef
KERNEL_TYPE ?= nano
CONF_FILE = prj_$(ARCH).conf
include $(ZEPHYR_BASE)/Makefile.inc

View file

@ -0,0 +1,5 @@
% Application : Buffer test
% TASK NAME PRIO ENTRY STACK GROUPS
% ===================================================
TASK MAIN 7 mainloop 2048 [EXE]

View file

@ -0,0 +1,2 @@
CONFIG_NET_BUF=y
CONFIG_BUF_DEBUG=y

View file

@ -0,0 +1,3 @@
ccflags-y += ${PROJECTINCLUDE}
obj-y = main.o

View file

@ -0,0 +1,89 @@
/* main.c - Application main entry point */
/*
* Copyright (c) 2015 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdint.h>
#include <stddef.h>
#include <misc/printk.h>
#include <net/buf.h>
struct bt_data {
void *hci_sync;
union {
uint16_t hci_opcode;
uint16_t acl_handle;
};
uint8_t type;
};
static int destroy_called;
static struct nano_fifo bufs_fifo;
static void buf_destroy(struct net_buf *buf)
{
destroy_called++;
if (buf->free != &bufs_fifo) {
printk("Invalid free pointer in buffer!\n");
}
}
static NET_BUF_POOL(bufs_pool, 22, 74, &bufs_fifo, buf_destroy,
sizeof(struct bt_data));
#ifdef CONFIG_MICROKERNEL
void mainloop(void)
#else
void main(void)
#endif
{
struct net_buf *bufs[ARRAY_SIZE(bufs_pool)];
int i;
printk("sizeof(struct net_buf) = %u\n", sizeof(struct net_buf));
printk("sizeof(bufs_pool) = %u\n", sizeof(bufs_pool));
net_buf_pool_init(bufs_pool);
for (i = 0; i < ARRAY_SIZE(bufs_pool); i++) {
struct net_buf *buf;
buf = net_buf_get(&bufs_fifo, 0);
if (!buf) {
printk("Failed to get buffer!\n");
return;
}
bufs[i] = buf;
}
for (i = 0; i < ARRAY_SIZE(bufs_pool); i++) {
net_buf_unref(bufs[i]);
}
if (destroy_called != ARRAY_SIZE(bufs_pool)) {
printk("Incorrect destroy callback count: %d\n",
destroy_called);
return;
}
printk("Buffer tests passed\n");
}

View file

@ -0,0 +1,6 @@
[test]
tags = buf
build_only = true
arch_whitelist = x86
# Doesn't work for ia32_pci
config_whitelist = CONFIG_PLATFORM="ia32"