mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #2285 from authmillenon/pktbuf/api/use-pkt
pktbuf: port to use pkt_t instead of void*
This commit is contained in:
commit
4f29a7757e
@ -34,9 +34,9 @@ extern "C" {
|
||||
#define UART0_BUFSIZE (32)
|
||||
#endif
|
||||
|
||||
#ifndef PKTBUF_SIZE
|
||||
#define PKTBUF_SIZE (2560) /* TODO: Make this value
|
||||
overall MTU dependent */
|
||||
#ifndef NG_PKTBUF_SIZE
|
||||
#define NG_PKTBUF_SIZE (2560) /* TODO: Make this value
|
||||
* overall MTU dependent */
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
|
@ -1,9 +1,6 @@
|
||||
ifneq (,$(filter pkt,$(USEMODULE)))
|
||||
DIRS += net/crosslayer/pkt
|
||||
endif
|
||||
ifneq (,$(filter pktbuf,$(USEMODULE)))
|
||||
DIRS += net/crosslayer/pktbuf
|
||||
endif
|
||||
ifneq (,$(filter pnet,$(USEMODULE)))
|
||||
DIRS += posix/pnet
|
||||
endif
|
||||
@ -74,6 +71,9 @@ endif
|
||||
ifneq (,$(filter ng_netreg,$(USEMODULE)))
|
||||
DIRS += net/crosslayer/ng_netreg
|
||||
endif
|
||||
ifneq (,$(filter ng_pktbuf,$(USEMODULE)))
|
||||
DIRS += net/crosslayer/ng_pktbuf
|
||||
endif
|
||||
ifneq (,$(filter netapi,$(USEMODULE)))
|
||||
DIRS += net/crosslayer/netapi
|
||||
endif
|
||||
|
@ -25,9 +25,6 @@ endif
|
||||
ifneq (,$(filter net_if,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/include
|
||||
endif
|
||||
ifneq (,$(filter pktbuf,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/include
|
||||
endif
|
||||
ifneq (,$(filter pktqueue,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/include
|
||||
endif
|
||||
|
@ -74,11 +74,17 @@ extern "C" {
|
||||
*
|
||||
* @note This type implements its own list implementation because of the way
|
||||
* it is stored in the packet buffer.
|
||||
* @note This type has no initializer on purpose. Please use @ref pktbuf
|
||||
* @note This type has no initializer on purpose. Please use @ref net_ng_pktbuf
|
||||
* as factory.
|
||||
*/
|
||||
/* packed to be aligned correctly in the static packet buffer */
|
||||
typedef struct __attribute__((packed)) ng_pktsnip {
|
||||
/**
|
||||
* @brief Counter of threads currently having control over this packet.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
unsigned int users;
|
||||
struct ng_pktsnip *next; /**< next snip in the packet */
|
||||
void *data; /**< pointer to the data of the snip */
|
||||
size_t size; /**< the length of the snip in byte */
|
||||
|
183
sys/include/net/ng_pktbuf.h
Normal file
183
sys/include/net/ng_pktbuf.h
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup net_ng_pktbuf Packet buffer
|
||||
* @ingroup net
|
||||
* @brief A global network packet buffer.
|
||||
*
|
||||
* @note **WARNING!!** Do not store data structures that are not packed
|
||||
* (defined with `__attribute__((packed))`) or enforce alignment in
|
||||
* in any way in here if @ref NG_PKTBUF_SIZE > 0. On some RISC architectures
|
||||
* this *will* lead to alignment problems and can potentially result
|
||||
* in segmentation/hard faults and other unexpected behaviour.
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Interface definition for the global network buffer. Network devices
|
||||
* and layers can allocate space for packets here.
|
||||
*
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*/
|
||||
#ifndef NG_PKTBUF_H_
|
||||
#define NG_PKTBUF_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "atomic.h"
|
||||
#include "cpu-conf.h"
|
||||
#include "net/ng_pkt.h"
|
||||
#include "net/ng_nettype.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def NG_PKTBUF_SIZE
|
||||
* @brief Maximum size of the static packet buffer.
|
||||
*
|
||||
* @details The rational here is to have at least space for 4 full-MTU IPv6
|
||||
* packages (2 incoming, 2 outgoing; 2 * 2 * 1280 B = 5 KiB) +
|
||||
* Meta-Data (roughly estimated to 1 KiB; might be smaller). If
|
||||
* @ref NG_PKTBUF_SIZE is 0 the packet buffer will use dynamic memory
|
||||
* management to allocate packets.
|
||||
*/
|
||||
#ifndef NG_PKTBUF_SIZE
|
||||
#define NG_PKTBUF_SIZE (6144)
|
||||
#endif /* NG_PKTBUF_SIZE */
|
||||
|
||||
/**
|
||||
* @brief Prepends a new ng_pktsnip_t to a packet.
|
||||
*
|
||||
* @details It is ill-advised to add a ng_pktsnip_t simply by using
|
||||
*
|
||||
* next = ng_pktsnip_add(NULL, NULL, size1, NG_NETTYPE_UNDEF);
|
||||
* pkt = ng_pktsnip_add(NULL, NULL, size2, NG_NETTYPE_UNDEF);
|
||||
*
|
||||
* pkt->next = next;
|
||||
* next->data = next->data + size2;
|
||||
*
|
||||
* Since @p data can be in the range of the data allocated on
|
||||
* ng_pktsnip_t::data of @p next, it would be impossible to free
|
||||
* ng_pktsnip_t::data of @p next, after @p next was released and the
|
||||
* generated ng_pktsnip_t not or vice versa. This function ensures that this
|
||||
* can't happen.
|
||||
*
|
||||
* @param[in] next The packet you want to add the ng_pktsnip_t to. If
|
||||
* ng_pktsnip_t::data field of @p next is equal to data it will
|
||||
* be set to `next->data + size`. If @p next is NULL the
|
||||
* ng_pktsnip_t::next field of the result will be also set to
|
||||
* NULL.
|
||||
* @param[in] data Data of the new ng_pktsnip_t. If @p data is NULL no data
|
||||
* will be inserted into the result.
|
||||
* @param[in] size Length of @p data. If @p size is 0, it will be assumed,
|
||||
* that @p data is NULL and no data will be inserted into the
|
||||
* result
|
||||
* @param[in] type Protocol type of the ng_pktsnip_t.
|
||||
*
|
||||
* @return Pointer to the packet part that represents the new ng_pktsnip_t.
|
||||
* @return NULL, if no space is left in the packet buffer.
|
||||
*/
|
||||
ng_pktsnip_t *ng_pktbuf_add(ng_pktsnip_t *next, void *data, size_t size,
|
||||
ng_nettype_t type);
|
||||
|
||||
/**
|
||||
* @brief Reallocates ng_pktsnip_t::data of @p pkt in the packet buffer, without
|
||||
* changing the content.
|
||||
*
|
||||
* @pre `pkt->users == 1 && pkt->next == NULL` and @p pkt must be in packet buffer
|
||||
*
|
||||
* @details If enough memory is available behind it or @p size is smaller than
|
||||
* the original size the packet then ng_pktsnip_t::data of @p pkt will
|
||||
* not be moved. Otherwise, it will be moved. If no space is available
|
||||
* nothing happens.
|
||||
*
|
||||
* @param[in] pkt A packet part.
|
||||
* @param[in] size The size for @p pkt.
|
||||
*
|
||||
* @return 0, on success
|
||||
* @return EINVAL, if precondition is not met
|
||||
* @return ENOENT, if ng_pktsnip_t::data of @p pkt was not from the packet buffer.
|
||||
* @return ENOMEM, if no space is left in the packet buffer or size was 0.
|
||||
*/
|
||||
int ng_pktbuf_realloc_data(ng_pktsnip_t *pkt, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Increases ng_pktsnip_t::users of @p pkt atomically.
|
||||
*
|
||||
* @param[in] pkt A packet.
|
||||
* @param[in] num Number you want to increment ng_pktsnip_t::users of @p pkt by.
|
||||
*/
|
||||
static inline void ng_pktbuf_hold(ng_pktsnip_t *pkt, unsigned int num)
|
||||
{
|
||||
if (pkt != NULL) {
|
||||
atomic_set_return(&(pkt->users), pkt->users + num);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decreases ng_pktsnip_t::users of @p pkt atomically and removes it if it
|
||||
* reaches 0.
|
||||
*
|
||||
* @param[in] pkt A packet.
|
||||
*/
|
||||
void ng_pktbuf_release(ng_pktsnip_t *pkt);
|
||||
|
||||
/**
|
||||
* @brief Must be called once before there is a write operation in a thread.
|
||||
*
|
||||
* @details This function duplicates a packet in the packet buffer if
|
||||
* ng_pktsnip_t::users of @p pkt > 1.
|
||||
*
|
||||
* @note Do *not* call this function in a thread twice on the same packet.
|
||||
*
|
||||
* @param[in] pkt The packet you want to write into.
|
||||
*
|
||||
* @return The (new) pointer to the pkt.
|
||||
* @return NULL, if ng_pktsnip_t::users of @p pkt > 1 and if there is not anough
|
||||
* space in the packet buffer.
|
||||
*/
|
||||
ng_pktsnip_t *ng_pktbuf_start_write(ng_pktsnip_t *pkt);
|
||||
|
||||
#ifdef DEVELHELP
|
||||
/**
|
||||
* @brief Prints some statistics about the packet buffer to stdout.
|
||||
*
|
||||
* @note Only available with DEVELHELP defined.
|
||||
*
|
||||
* @details Statistics include maximum number of reserved bytes.
|
||||
*/
|
||||
void ng_pktbuf_stats(void);
|
||||
#endif
|
||||
|
||||
/* for testing */
|
||||
#ifdef TEST_SUITES
|
||||
/**
|
||||
* @brief Checks if packet buffer is empty
|
||||
*
|
||||
* @return true, if packet buffer is empty
|
||||
* @return false, if packet buffer is not empty
|
||||
*/
|
||||
bool ng_pktbuf_is_empty(void);
|
||||
|
||||
/**
|
||||
* @brief Resets the whole packet buffer
|
||||
*/
|
||||
void ng_pktbuf_reset(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NG_PKTBUF_H_ */
|
||||
/** @} */
|
299
sys/net/crosslayer/ng_pktbuf/_pktbuf_dynamic.c
Normal file
299
sys/net/crosslayer/ng_pktbuf/_pktbuf_dynamic.c
Normal file
@ -0,0 +1,299 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Martin Lenders <mlenders@inf.fu-berlin.de>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup net_ng_pktbuf
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "_pktbuf_internal.h"
|
||||
#include "net/ng_pktbuf.h"
|
||||
#include "utlist.h"
|
||||
|
||||
#if NG_PKTBUF_SIZE == 0
|
||||
/* chunk table to allow for free(ptr + x)-like behaviour */
|
||||
typedef struct __attribute__((packed)) _chunk_list_t {
|
||||
struct _chunk_list_t *next;
|
||||
uint8_t *ptr;
|
||||
} _chunk_list_t;
|
||||
|
||||
typedef struct __attribute__((packed)) _chunk_table_t {
|
||||
struct _chunk_table_t *next;
|
||||
uint8_t *range_start;
|
||||
size_t range_len;
|
||||
_chunk_list_t *chunks;
|
||||
uint8_t used;
|
||||
} _chunk_table_t;
|
||||
|
||||
static _chunk_table_t *_chunk_table = NULL;
|
||||
|
||||
/* this organizes chunks, since free(ptr + x) is not possible on most platforms */
|
||||
static _chunk_table_t *_create_table_entry(void *pkt, size_t size);
|
||||
static _chunk_table_t *_find_chunk(const uint8_t *chunk, _chunk_table_t **prev,
|
||||
_chunk_list_t **node_res);
|
||||
static inline bool _in_range(_chunk_table_t *entry, uint8_t *ptr);
|
||||
|
||||
void *_pktbuf_internal_alloc(size_t size)
|
||||
{
|
||||
_chunk_table_t *entry;
|
||||
void *data;
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
data = malloc(size);
|
||||
|
||||
if (data == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
entry = _create_table_entry(data, size);
|
||||
|
||||
if (entry == NULL) {
|
||||
free(data);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* cppcheck-suppress memleak entry will be freed eventually in _pktbuf_internal_free().
|
||||
* Checked with valgrind. */
|
||||
return data;
|
||||
}
|
||||
|
||||
void *_pktbuf_internal_realloc(void *ptr, size_t size)
|
||||
{
|
||||
_chunk_list_t *node = NULL;
|
||||
void *new = NULL;
|
||||
_chunk_table_t *entry;
|
||||
|
||||
if (size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
entry = _find_chunk(ptr, NULL, &node);
|
||||
|
||||
/* entry can't be NULL since prelimanary _pktbuf_internal_contains() check ensures that */
|
||||
if ((ptr == entry->range_start) && (entry->chunks == NULL)) {
|
||||
new = realloc(entry->range_start, size);
|
||||
|
||||
if (new == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
entry->range_start = new;
|
||||
entry->range_len = size;
|
||||
}
|
||||
else {
|
||||
size_t range_len = entry->range_len;
|
||||
|
||||
if (node != NULL) {
|
||||
range_len -= (node->ptr - entry->range_start);
|
||||
}
|
||||
|
||||
new = malloc(size);
|
||||
|
||||
if (new == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
entry = _create_table_entry(new, size);
|
||||
|
||||
if (entry == NULL) {
|
||||
free(new);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(new, ptr, (size < range_len) ? size : range_len);
|
||||
_pktbuf_internal_free(ptr);
|
||||
}
|
||||
|
||||
return new;
|
||||
/* cppcheck-suppress memleak entry will be freed eventually in _pktbuf_internal_free().
|
||||
* Checked with valgrind. */
|
||||
}
|
||||
|
||||
bool _pktbuf_internal_add_pkt(void *ptr)
|
||||
{
|
||||
_chunk_table_t *entry = _chunk_table;
|
||||
|
||||
while (entry != NULL) {
|
||||
if (_in_range(entry, ptr)) {
|
||||
_chunk_list_t *node = malloc(sizeof(_chunk_list_t));
|
||||
|
||||
if (node == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
node->ptr = ptr;
|
||||
LL_PREPEND(entry->chunks, node);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void _pktbuf_internal_free(void *ptr)
|
||||
{
|
||||
_chunk_list_t *node = NULL;
|
||||
_chunk_table_t *prev = NULL, *entry = _find_chunk(ptr, &prev, &node);
|
||||
|
||||
if (node != NULL) {
|
||||
LL_DELETE(entry->chunks, node);
|
||||
free(node);
|
||||
}
|
||||
else if (entry->range_start == ptr) {
|
||||
entry->used = 0;
|
||||
}
|
||||
|
||||
if (entry->chunks == NULL && entry->used == 0) {
|
||||
if (prev == NULL) {
|
||||
if (entry->next == NULL) {
|
||||
_chunk_table = NULL;
|
||||
}
|
||||
else {
|
||||
_chunk_table = entry->next;
|
||||
}
|
||||
}
|
||||
else {
|
||||
prev->next = entry->next;
|
||||
}
|
||||
|
||||
free(entry->range_start);
|
||||
free(entry);
|
||||
}
|
||||
}
|
||||
|
||||
bool _pktbuf_internal_contains(const void *ptr)
|
||||
{
|
||||
return (_find_chunk(ptr, NULL, NULL) != NULL);
|
||||
}
|
||||
|
||||
#ifdef DEVELHELP
|
||||
void _pktbuf_internal_stats(void)
|
||||
{
|
||||
printf("Dynamic packet buffer\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TEST_SUITES
|
||||
bool _pktbuf_internal_is_empty(void)
|
||||
{
|
||||
return (_chunk_table == NULL);
|
||||
}
|
||||
|
||||
void _pktbuf_internal_reset(void)
|
||||
{
|
||||
_chunk_table_t *entry = _chunk_table;
|
||||
|
||||
while (entry != NULL) {
|
||||
_chunk_table_t *next = entry->next;
|
||||
_chunk_list_t *node = entry->chunks;
|
||||
free(entry->range_start);
|
||||
|
||||
while (entry->chunks != NULL) {
|
||||
LL_DELETE(entry->chunks, node);
|
||||
free(node);
|
||||
}
|
||||
|
||||
free(entry);
|
||||
entry = next;
|
||||
}
|
||||
|
||||
_chunk_table = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static _chunk_table_t *_create_table_entry(void *data, size_t size)
|
||||
{
|
||||
_chunk_table_t *entry = (_chunk_table_t *)malloc(sizeof(_chunk_table_t));
|
||||
|
||||
if (entry == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (_chunk_table == NULL) {
|
||||
entry->next = NULL;
|
||||
_chunk_table = entry;
|
||||
}
|
||||
else {
|
||||
entry->next = _chunk_table;
|
||||
_chunk_table = entry;
|
||||
}
|
||||
|
||||
entry->range_start = data;
|
||||
entry->range_len = size;
|
||||
entry->chunks = NULL;
|
||||
entry->used = 1;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static _chunk_table_t *_find_chunk(const uint8_t *chunk, _chunk_table_t **prev,
|
||||
_chunk_list_t **node_res)
|
||||
{
|
||||
_chunk_table_t *entry = _chunk_table;
|
||||
|
||||
if (prev != NULL) {
|
||||
*prev = NULL;
|
||||
}
|
||||
|
||||
while (entry != NULL) {
|
||||
_chunk_list_t *node = entry->chunks;
|
||||
|
||||
if (entry->range_start == chunk) {
|
||||
if (node_res != NULL) {
|
||||
*node_res = NULL;
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
while (node != NULL) {
|
||||
if (node->ptr == chunk) {
|
||||
if (node_res != NULL) {
|
||||
*node_res = node;
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
if (prev != NULL) {
|
||||
*prev = entry;
|
||||
}
|
||||
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline bool _in_range(_chunk_table_t *entry, uint8_t *ptr)
|
||||
{
|
||||
return (entry != NULL) &&
|
||||
(ptr >= entry->range_start) &&
|
||||
(ptr < (entry->range_start + entry->range_len));
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
107
sys/net/crosslayer/ng_pktbuf/_pktbuf_internal.h
Normal file
107
sys/net/crosslayer/ng_pktbuf/_pktbuf_internal.h
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Martin Lenders <mlenders@inf.fu-berlin.de>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup net_ng_pktbuf
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Internal definitions for the packet buffer
|
||||
*
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*/
|
||||
#ifndef XPKTBUF_INTERNAL_H_
|
||||
#define XPKTBUF_INTERNAL_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Internal alloc on packet buffer
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html">
|
||||
* malloc()
|
||||
* </a>
|
||||
*/
|
||||
void *_pktbuf_internal_alloc(size_t size);
|
||||
|
||||
/**
|
||||
* @brief Internal realloc on static packet buffer
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/realloc.html">
|
||||
* realloc()
|
||||
* </a>
|
||||
*/
|
||||
void *_pktbuf_internal_realloc(void *ptr, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Adds packet that uses @p ptr for its data part
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
bool _pktbuf_internal_add_pkt(void *ptr);
|
||||
|
||||
/**
|
||||
* @brief Internal free on static packet buffer
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html">
|
||||
* free()
|
||||
* </a> but @p ptr can be anywhere in alloced space.
|
||||
*/
|
||||
void _pktbuf_internal_free(void *ptr);
|
||||
|
||||
/**
|
||||
* @brief Checks if a pointer is part of the static packet buffer.
|
||||
*
|
||||
* @param[in] ptr A pointer.
|
||||
*
|
||||
* @return true, if @p ptr is part of the static packet buffer.
|
||||
* @return false, if @p ptr is not part of the static packet buffer.
|
||||
*/
|
||||
bool _pktbuf_internal_contains(const void *ptr);
|
||||
|
||||
#ifdef DEVELHELP
|
||||
/**
|
||||
* @brief Prints some statistics about the packet buffer to stdout.
|
||||
*
|
||||
* @details Statistics include maximum number of reserved bytes.
|
||||
*/
|
||||
void _pktbuf_internal_stats(void);
|
||||
#endif
|
||||
|
||||
/* for testing */
|
||||
#ifdef TEST_SUITES
|
||||
/**
|
||||
* @brief Checks if packet buffer is empty
|
||||
*
|
||||
* @return 1, if packet buffer is empty
|
||||
* @return 0, if packet buffer is not empty
|
||||
*/
|
||||
bool _pktbuf_internal_is_empty(void);
|
||||
|
||||
/**
|
||||
* @brief Sets the whole packet buffer to 0
|
||||
*/
|
||||
void _pktbuf_internal_reset(void);
|
||||
#endif /* TEST_SUITES */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* XPKTBUF_INTERNAL_H_ */
|
||||
/** @} */
|
298
sys/net/crosslayer/ng_pktbuf/_pktbuf_static.c
Normal file
298
sys/net/crosslayer/ng_pktbuf/_pktbuf_static.c
Normal file
@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Martin Lenders <mlenders@inf.fu-berlin.de>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup net_ng_pktbuf
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "net/ng_pktbuf.h"
|
||||
#include "_pktbuf_internal.h"
|
||||
|
||||
/* only for static packet buffer */
|
||||
#if NG_PKTBUF_SIZE > 0
|
||||
|
||||
#define _PKTBUF_ALIGN_BYTES (sizeof(void *))
|
||||
|
||||
#ifdef DEVELHELP
|
||||
static unsigned int _pktbuf_max_bytes = 0;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Data type to represent used chunks in packet buffer.
|
||||
*/
|
||||
typedef struct __attribute__((packed)) _used_t {
|
||||
struct _used_t *next;
|
||||
uint16_t size;
|
||||
uint8_t pkts;
|
||||
uint8_t _align; /* alignment */
|
||||
} _used_t;
|
||||
|
||||
static uint8_t _buf[NG_PKTBUF_SIZE];
|
||||
|
||||
/**
|
||||
* @brief Get first element in buffer
|
||||
*/
|
||||
static inline _used_t *_head(void)
|
||||
{
|
||||
return (_used_t *)_buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get data part (memory behind `_used_t` descriptive header) of a packet
|
||||
*/
|
||||
static inline void *_data(_used_t *node)
|
||||
{
|
||||
return (void *)(((_used_t *)node) + 1);
|
||||
}
|
||||
|
||||
static inline void *_data_end(_used_t *node)
|
||||
{
|
||||
return (void *)(((uint8_t *)_data(node)) + node->size);
|
||||
}
|
||||
|
||||
static inline bool _in_data_range(_used_t *node, const void *ptr)
|
||||
{
|
||||
return (ptr >= _data(node)) && (ptr < _data_end(node));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Size with metadata of allocation
|
||||
*/
|
||||
static inline size_t _total_sz(uint16_t sz)
|
||||
{
|
||||
return sizeof(_used_t) + sz;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief _used_t typed alias for _total_sz
|
||||
*/
|
||||
static inline size_t __total_sz(_used_t *node)
|
||||
{
|
||||
return _total_sz(node->size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief aligned size with metadata
|
||||
*/
|
||||
static inline size_t __al_total_sz(_used_t *node)
|
||||
{
|
||||
size_t size = __total_sz(node);
|
||||
|
||||
if (size % _PKTBUF_ALIGN_BYTES) {
|
||||
return size + (_PKTBUF_ALIGN_BYTES - (size % _PKTBUF_ALIGN_BYTES));
|
||||
}
|
||||
else {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Index of an allocation's first byte in buffer
|
||||
*/
|
||||
static inline unsigned int _start_idx(_used_t *node)
|
||||
{
|
||||
return (int)(((uint8_t *)node) - _buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Index of an allocation's last byte in buffer
|
||||
*/
|
||||
static inline unsigned int _end_idx(_used_t *node)
|
||||
{
|
||||
return _start_idx(node) + __total_sz(node) - 1;
|
||||
}
|
||||
|
||||
static _used_t *_find(_used_t **prev_ptr, _used_t **node_ptr, const void *ptr)
|
||||
{
|
||||
_used_t *node = _head(), *prev = NULL;
|
||||
|
||||
if (ptr != NULL) {
|
||||
while (node != NULL) {
|
||||
if (_in_data_range(node, ptr)) {
|
||||
*prev_ptr = prev;
|
||||
*node_ptr = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
prev = node;
|
||||
node = (_used_t *)node->next;
|
||||
}
|
||||
}
|
||||
|
||||
*prev_ptr = NULL;
|
||||
*node_ptr = NULL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *_pktbuf_internal_alloc(size_t size)
|
||||
{
|
||||
_used_t *node = _head(), *old_next, *new_next;
|
||||
|
||||
if ((size == 0) || (size > NG_PKTBUF_SIZE)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (node->size == 0) { /* if head is currently not initialized */
|
||||
if (node->next == NULL || (_start_idx(node->next) >= _total_sz(size))) {
|
||||
/* if enough space is there */
|
||||
node->size = size; /* just take it */
|
||||
node->pkts = 1;
|
||||
|
||||
return _data(node);
|
||||
}
|
||||
else if (node->next != NULL) {
|
||||
/* else go directly to next allocation if it exists */
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
while ((node->next != NULL)
|
||||
/* and if space between current and next allocation is not big enough */
|
||||
&& ((_start_idx(node->next) - _end_idx(node)) < _total_sz(size))) {
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
/* jump ahead size of current packet */
|
||||
new_next = (_used_t *)(((uint8_t *)node) + __al_total_sz(node));
|
||||
|
||||
if ((((uint8_t *)new_next) + size) > (((uint8_t *)_head()) + NG_PKTBUF_SIZE)) {
|
||||
/* new packet does not fit into _pktbuf */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
old_next = node->next;
|
||||
node->next = new_next;
|
||||
node->next->next = old_next;
|
||||
|
||||
node = new_next;
|
||||
node->size = size;
|
||||
node->pkts = 1;
|
||||
|
||||
#ifdef DEVELHELP
|
||||
|
||||
if ((_end_idx(node) + 1) > _pktbuf_max_bytes) {
|
||||
_pktbuf_max_bytes = _end_idx(node) + 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return _data(node);
|
||||
}
|
||||
|
||||
bool _pktbuf_internal_add_pkt(void *ptr)
|
||||
{
|
||||
_used_t *prev, *node;
|
||||
|
||||
if (_find(&prev, &node, ptr) != NULL) {
|
||||
(node->pkts)++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void _free_helper(_used_t *prev, _used_t *node)
|
||||
{
|
||||
if ((--(node->pkts)) == 0) {
|
||||
if (prev == NULL) {
|
||||
node->size = 0;
|
||||
}
|
||||
else {
|
||||
prev->next = node->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _pktbuf_internal_free(void *ptr)
|
||||
{
|
||||
_used_t *prev, *node;
|
||||
|
||||
if (_find(&prev, &node, ptr) != NULL) {
|
||||
_free_helper(prev, node);
|
||||
}
|
||||
}
|
||||
|
||||
void *_pktbuf_internal_realloc(void *ptr, size_t size)
|
||||
{
|
||||
_used_t *new, *prev, *orig = NULL;
|
||||
|
||||
if ((size == 0) || (size > NG_PKTBUF_SIZE)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_find(&prev, &orig, ptr);
|
||||
|
||||
if ((orig != NULL) &&
|
||||
((orig->size >= size) /* size in orig is sufficient */
|
||||
|| ((orig->next == NULL) /* or orig is last packet and buffer space is sufficient */
|
||||
&& ((_start_idx(orig) + _total_sz(size)) < NG_PKTBUF_SIZE))
|
||||
|| ((orig->next != NULL) /* or space between orig and orig->next is sufficient */
|
||||
&& ((_start_idx(orig->next) - _start_idx(orig)) >= _total_sz(size))))) {
|
||||
orig->size = size;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
new = _pktbuf_internal_alloc(size);
|
||||
|
||||
if (new != NULL) {
|
||||
if (orig != NULL) {
|
||||
memcpy(_data(new), ptr, (orig->size < size) ? orig->size : size);
|
||||
_free_helper(prev, orig);
|
||||
}
|
||||
|
||||
return _data(new);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool _pktbuf_internal_contains(const void *ptr)
|
||||
{
|
||||
return ((_buf < ((uint8_t *)ptr)) && (((uint8_t *)ptr) <= &(_buf[NG_PKTBUF_SIZE - 1])));
|
||||
}
|
||||
|
||||
#ifdef DEVELHELP
|
||||
void _pktbuf_internal_stats(void)
|
||||
{
|
||||
printf("Static packet buffer\n");
|
||||
printf(" * Maximum number of reserved bytes: %u\n", _pktbuf_max_bytes);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* for testing */
|
||||
#ifdef TEST_SUITES
|
||||
bool _pktbuf_internal_is_empty(void)
|
||||
{
|
||||
return ((_head()->next == NULL) && (_head()->size == 0));
|
||||
}
|
||||
|
||||
void _pktbuf_internal_reset(void)
|
||||
{
|
||||
_head()->next = NULL;
|
||||
_head()->size = 0;
|
||||
#ifdef DEVELHELP
|
||||
_pktbuf_max_bytes = 0;
|
||||
#endif
|
||||
}
|
||||
#endif /* TEST_SUITES */
|
||||
#endif /* NG_PKTBUF_SIZE > 0 */
|
||||
|
||||
/** @} */
|
261
sys/net/crosslayer/ng_pktbuf/ng_pktbuf.c
Normal file
261
sys/net/crosslayer/ng_pktbuf/ng_pktbuf.c
Normal file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup net_ng_pktbuf
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "clist.h"
|
||||
#include "mutex.h"
|
||||
#include "net/ng_nettype.h"
|
||||
#include "net/ng_pkt.h"
|
||||
#include "utlist.h"
|
||||
|
||||
#include "net/ng_pktbuf.h"
|
||||
|
||||
#include "_pktbuf_internal.h"
|
||||
|
||||
static mutex_t _pktbuf_mutex = MUTEX_INIT;
|
||||
|
||||
/* internal ng_pktbuf functions */
|
||||
static ng_pktsnip_t *_pktbuf_alloc(size_t size);
|
||||
static ng_pktsnip_t *_pktbuf_add_unsafe(ng_pktsnip_t *pkt, void *data,
|
||||
size_t size, ng_nettype_t type);
|
||||
static ng_pktsnip_t *_pktbuf_duplicate(const ng_pktsnip_t *pkt);
|
||||
|
||||
int ng_pktbuf_realloc_data(ng_pktsnip_t *pkt, size_t size)
|
||||
{
|
||||
void *new;
|
||||
|
||||
if (pkt == NULL || !_pktbuf_internal_contains(pkt->data)) {
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
if (pkt->users > 1 || pkt->next != NULL) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&_pktbuf_mutex);
|
||||
|
||||
new = _pktbuf_internal_realloc(pkt->data, size);
|
||||
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
|
||||
if (new == NULL) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
pkt->data = new;
|
||||
pkt->size = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ng_pktsnip_t *ng_pktbuf_add(ng_pktsnip_t *next, void *data, size_t size,
|
||||
ng_nettype_t type)
|
||||
{
|
||||
ng_pktsnip_t *snip;
|
||||
|
||||
mutex_lock(&_pktbuf_mutex);
|
||||
|
||||
snip = _pktbuf_add_unsafe(next, data, size, type);
|
||||
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
|
||||
return snip;
|
||||
}
|
||||
|
||||
void ng_pktbuf_release(ng_pktsnip_t *pkt)
|
||||
{
|
||||
if (pkt == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_set_return(&(pkt->users), pkt->users - 1);
|
||||
|
||||
if (pkt->users == 0 && _pktbuf_internal_contains(pkt->data)) {
|
||||
mutex_lock(&_pktbuf_mutex);
|
||||
|
||||
_pktbuf_internal_free(pkt->data);
|
||||
_pktbuf_internal_free(pkt);
|
||||
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
ng_pktsnip_t *ng_pktbuf_start_write(ng_pktsnip_t *pkt)
|
||||
{
|
||||
if (pkt != NULL && pkt->users > 1) {
|
||||
ng_pktsnip_t *res = NULL;
|
||||
|
||||
mutex_lock(&_pktbuf_mutex);
|
||||
|
||||
res = _pktbuf_duplicate(pkt);
|
||||
|
||||
atomic_set_return(&pkt->users, pkt->users - 1);
|
||||
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return pkt;
|
||||
}
|
||||
|
||||
/***********************************
|
||||
* internal ng_pktbuf functions *
|
||||
***********************************/
|
||||
|
||||
static ng_pktsnip_t *_pktbuf_alloc(size_t size)
|
||||
{
|
||||
ng_pktsnip_t *pkt;
|
||||
|
||||
pkt = (ng_pktsnip_t *)_pktbuf_internal_alloc(sizeof(ng_pktsnip_t));
|
||||
|
||||
if (pkt == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pkt->data = _pktbuf_internal_alloc(size);
|
||||
|
||||
if (pkt->data == NULL) {
|
||||
_pktbuf_internal_free(pkt);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pkt->next = NULL;
|
||||
pkt->size = size;
|
||||
pkt->users = 1;
|
||||
|
||||
return pkt;
|
||||
}
|
||||
|
||||
static ng_pktsnip_t *_pktbuf_add_unsafe(ng_pktsnip_t *next, void *data,
|
||||
size_t size, ng_nettype_t type)
|
||||
{
|
||||
ng_pktsnip_t *snip;
|
||||
|
||||
if (size == 0) {
|
||||
data = 0;
|
||||
}
|
||||
|
||||
snip = (ng_pktsnip_t *)_pktbuf_internal_alloc(sizeof(ng_pktsnip_t));
|
||||
|
||||
if (snip == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (next == NULL || next->data != data) {
|
||||
if (size != 0) {
|
||||
snip->data = _pktbuf_internal_alloc(size);
|
||||
|
||||
if (snip->data == NULL) {
|
||||
_pktbuf_internal_free(snip);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (data != NULL) {
|
||||
memcpy(snip->data, data, size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
snip->data = data;
|
||||
}
|
||||
}
|
||||
else {
|
||||
snip->data = data;
|
||||
|
||||
next->size -= size;
|
||||
next->data = (void *)(((uint8_t *)next->data) + size);
|
||||
|
||||
if (!_pktbuf_internal_add_pkt(next->data)) {
|
||||
_pktbuf_internal_free(snip);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
snip->next = NULL;
|
||||
snip->size = size;
|
||||
snip->type = type;
|
||||
snip->users = 1;
|
||||
|
||||
LL_PREPEND(next, snip);
|
||||
|
||||
return snip;
|
||||
}
|
||||
|
||||
static ng_pktsnip_t *_pktbuf_duplicate(const ng_pktsnip_t *pkt)
|
||||
{
|
||||
ng_pktsnip_t *res = NULL;
|
||||
|
||||
res = _pktbuf_alloc(pkt->size);
|
||||
|
||||
if (res == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(res->data, pkt->data, pkt->size);
|
||||
res->type = pkt->type;
|
||||
|
||||
while (pkt->next) {
|
||||
ng_pktsnip_t *header = NULL;
|
||||
|
||||
pkt = pkt->next;
|
||||
header = _pktbuf_add_unsafe(res, pkt->data, pkt->size, pkt->type);
|
||||
|
||||
if (header == NULL) {
|
||||
do {
|
||||
ng_pktsnip_t *next = res->next;
|
||||
|
||||
_pktbuf_internal_free(res->data);
|
||||
_pktbuf_internal_free(res);
|
||||
|
||||
res = next;
|
||||
} while (res);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifdef DEVELHELP
|
||||
void ng_pktbuf_stats(void)
|
||||
{
|
||||
_pktbuf_internal_stats();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TEST_SUITES
|
||||
bool ng_pktbuf_is_empty(void)
|
||||
{
|
||||
return _pktbuf_internal_is_empty();
|
||||
}
|
||||
|
||||
void ng_pktbuf_reset(void)
|
||||
{
|
||||
_pktbuf_internal_reset();
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
@ -1,517 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file pktbuf.c
|
||||
*
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mutex.h"
|
||||
#include "pktbuf.h"
|
||||
|
||||
#if PKTBUF_SIZE < 128
|
||||
typedef uint8_t _pktsize_t;
|
||||
#elif PKTBUF_SIZE < 65536
|
||||
typedef uint16_t _pktsize_t;
|
||||
#else
|
||||
typedef size_t _pktsize_t;
|
||||
#endif
|
||||
|
||||
typedef struct __attribute__((packed)) _packet_t {
|
||||
volatile struct _packet_t *next;
|
||||
uint8_t processing;
|
||||
_pktsize_t size;
|
||||
} _packet_t;
|
||||
|
||||
static uint8_t _pktbuf[PKTBUF_SIZE];
|
||||
static mutex_t _pktbuf_mutex = MUTEX_INIT;
|
||||
|
||||
/**
|
||||
* @brief Get first element in packet buffer.
|
||||
*/
|
||||
static inline _packet_t *_pktbuf_head(void)
|
||||
{
|
||||
return (_packet_t *)(&(_pktbuf[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get data part (memory behind `_packet_t` descriptive header) of a packet
|
||||
*
|
||||
* @param[in] pkt A packet
|
||||
*
|
||||
* @return Data part of the packet.
|
||||
*/
|
||||
static inline void *_pkt_data(_packet_t *pkt)
|
||||
{
|
||||
return (void *)(((_packet_t *)pkt) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates total size (*size* + size of `_packet_t` descriptive header) of a
|
||||
* packet in memory
|
||||
*
|
||||
* @param[in] size A given packet size
|
||||
*
|
||||
* @return Total size of a packet in memory.
|
||||
*/
|
||||
static inline size_t _pkt_total_sz(size_t size)
|
||||
{
|
||||
return sizeof(_packet_t) + size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get pointer to the byte after the last byte of a packet.
|
||||
*
|
||||
* @param[in] pkt A packet
|
||||
*
|
||||
* @return Pointer to the last byte of a packet
|
||||
*/
|
||||
static inline void *_pkt_end(_packet_t *pkt)
|
||||
{
|
||||
return (void *)((uint8_t *)pkt + _pkt_total_sz(pkt->size));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get index in packet buffer of the first byte of a packet's data part
|
||||
*
|
||||
* @param[in] pkt A packet
|
||||
*
|
||||
* @return Index in packet buffer of the first byte of *pkt*'s data part.
|
||||
*/
|
||||
static inline size_t _pktbuf_start_idx(_packet_t *pkt)
|
||||
{
|
||||
return (size_t)(((uint8_t *)pkt) - (&(_pktbuf[0])));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get index in packet buffer of the last byte of a packet's data part
|
||||
*
|
||||
* @param[in] pkt A packet
|
||||
*
|
||||
* @return Index in packet buffer of the last byte of *pkt*'s data part.
|
||||
*/
|
||||
static inline size_t _pktbuf_end_idx(_packet_t *pkt)
|
||||
{
|
||||
return _pktbuf_start_idx(pkt) + _pkt_total_sz(pkt->size) - 1;
|
||||
}
|
||||
|
||||
static _packet_t *_pktbuf_find_with_prev(_packet_t **prev_ptr,
|
||||
_packet_t **packet_ptr, const void *pkt)
|
||||
{
|
||||
_packet_t *packet = _pktbuf_head(), *prev = NULL;
|
||||
|
||||
while (packet != NULL) {
|
||||
|
||||
if (_pkt_data(packet) == pkt) {
|
||||
*prev_ptr = prev;
|
||||
*packet_ptr = packet;
|
||||
return packet;
|
||||
}
|
||||
|
||||
prev = packet;
|
||||
packet = (_packet_t *)packet->next;
|
||||
}
|
||||
|
||||
*prev_ptr = NULL;
|
||||
*packet_ptr = NULL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static _packet_t *_pktbuf_find(const void *pkt)
|
||||
{
|
||||
_packet_t *packet = _pktbuf_head();
|
||||
|
||||
#ifdef DEVELHELP
|
||||
|
||||
if (pkt == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* DEVELHELP */
|
||||
|
||||
while (packet != NULL) {
|
||||
if ((_pkt_data(packet) <= pkt) && (pkt < _pkt_end(packet))) {
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
return packet;
|
||||
}
|
||||
|
||||
packet = (_packet_t *)packet->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static _packet_t *_pktbuf_alloc(size_t size)
|
||||
{
|
||||
_packet_t *packet = _pktbuf_head(), *old_next;
|
||||
|
||||
if ((size == 0) || (size > PKTBUF_SIZE)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((packet->size == 0) /* if first packet is currently not initialized
|
||||
* but next packet and no space between first
|
||||
* and next in its slot */
|
||||
&& (packet->processing == 0)
|
||||
&& (packet->next != NULL)
|
||||
&& ((_pktbuf_start_idx((_packet_t *)(packet->next)) - _pkt_total_sz(packet->size)) < size)) {
|
||||
packet = (_packet_t *)packet->next;
|
||||
}
|
||||
|
||||
/* while packet is not initialized */
|
||||
while ((packet->processing > 0) || (packet->size > size)) {
|
||||
old_next = (_packet_t *)packet->next;
|
||||
|
||||
/* if current packet is the last in buffer, but buffer space is exceeded */
|
||||
if ((old_next == NULL)
|
||||
&& ((_pktbuf_end_idx(packet) + _pkt_total_sz(size)) > PKTBUF_SIZE)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* if current packet is the last in the buffer or if space between
|
||||
* current packet and next packet is big enough */
|
||||
if ((old_next == NULL)
|
||||
|| ((_pktbuf_start_idx((_packet_t *)(packet->next)) - _pktbuf_end_idx(packet)) >= _pkt_total_sz(
|
||||
size))) {
|
||||
|
||||
_packet_t *new_next = (_packet_t *)(((uint8_t *)packet) + _pkt_total_sz(packet->size));
|
||||
/* jump ahead size of current packet. */
|
||||
packet->next = new_next;
|
||||
packet->next->next = old_next;
|
||||
|
||||
packet = new_next;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
packet = old_next;
|
||||
}
|
||||
|
||||
packet->size = size;
|
||||
packet->processing = 1;
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
static void _pktbuf_free(_packet_t *prev, _packet_t *packet)
|
||||
{
|
||||
if ((packet->processing)-- > 1) { /* `> 1` because packet->processing may already
|
||||
* be 0 in which case --(packet->processing)
|
||||
* would wrap to 255. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (prev == NULL) { /* packet is _pktbuf_head() */
|
||||
packet->size = 0;
|
||||
}
|
||||
else {
|
||||
prev->next = packet->next;
|
||||
}
|
||||
}
|
||||
|
||||
int pktbuf_contains(const void *pkt)
|
||||
{
|
||||
return ((&(_pktbuf[0]) < ((uint8_t *)pkt)) && (((uint8_t *)pkt) <= &(_pktbuf[PKTBUF_SIZE - 1])));
|
||||
}
|
||||
|
||||
void *pktbuf_alloc(size_t size)
|
||||
{
|
||||
_packet_t *packet;
|
||||
|
||||
mutex_lock(&_pktbuf_mutex);
|
||||
|
||||
packet = _pktbuf_alloc(size);
|
||||
|
||||
if (packet == NULL) {
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
|
||||
return _pkt_data(packet);
|
||||
}
|
||||
|
||||
void *pktbuf_realloc(const void *pkt, size_t size)
|
||||
{
|
||||
_packet_t *new, *prev, *orig;
|
||||
|
||||
mutex_lock(&_pktbuf_mutex);
|
||||
|
||||
if ((size == 0) || (size > PKTBUF_SIZE)) {
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_pktbuf_find_with_prev(&prev, &orig, pkt);
|
||||
|
||||
if ((orig != NULL) &&
|
||||
((orig->size >= size) /* and *orig* is last packet, and space in
|
||||
* _pktbuf is sufficient */
|
||||
|| ((orig->next == NULL)
|
||||
&& ((_pktbuf_start_idx(orig) + _pkt_total_sz(size)) < PKTBUF_SIZE))
|
||||
|| ((orig->next != NULL) /* or space between pointer and the next is big enough: */
|
||||
&& ((_pktbuf_start_idx((_packet_t *)(orig->next)) - _pktbuf_start_idx(orig))
|
||||
>= _pkt_total_sz(size))))) {
|
||||
orig->size = size;
|
||||
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
return (void *)pkt;
|
||||
}
|
||||
|
||||
new = _pktbuf_alloc(size);
|
||||
|
||||
if (new == NULL) {
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (orig != NULL) {
|
||||
memcpy(_pkt_data(new), _pkt_data(orig), orig->size);
|
||||
|
||||
_pktbuf_free(prev, orig);
|
||||
}
|
||||
else {
|
||||
memcpy(_pkt_data(new), pkt, size);
|
||||
}
|
||||
|
||||
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
|
||||
return _pkt_data(new);
|
||||
}
|
||||
|
||||
void *pktbuf_insert(const void *data, size_t size)
|
||||
{
|
||||
_packet_t *packet;
|
||||
|
||||
if ((data == NULL) || (size == 0)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mutex_lock(&_pktbuf_mutex);
|
||||
packet = _pktbuf_alloc(size);
|
||||
|
||||
if (packet == NULL) {
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(_pkt_data(packet), data, size);
|
||||
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
return _pkt_data(packet);
|
||||
}
|
||||
|
||||
int pktbuf_copy(void *pkt, const void *data, size_t data_len)
|
||||
{
|
||||
_packet_t *packet;
|
||||
|
||||
mutex_lock(&_pktbuf_mutex);
|
||||
|
||||
#ifdef DEVELHELP
|
||||
|
||||
if (data == NULL) {
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (pkt == NULL) {
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#endif /* DEVELHELP */
|
||||
|
||||
packet = _pktbuf_find(pkt);
|
||||
|
||||
|
||||
if ((packet != NULL) && (packet->size > 0) && (packet->processing > 0)) {
|
||||
/* packet space not engough? */
|
||||
if (data_len > (size_t)(((uint8_t *)_pkt_end(packet)) - ((uint8_t *)pkt))) {
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
memcpy(pkt, data, data_len);
|
||||
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
return data_len;
|
||||
}
|
||||
|
||||
void pktbuf_hold(const void *pkt)
|
||||
{
|
||||
_packet_t *packet;
|
||||
|
||||
mutex_lock(&_pktbuf_mutex);
|
||||
|
||||
packet = _pktbuf_find(pkt);
|
||||
|
||||
if (packet != NULL) {
|
||||
packet->processing++;
|
||||
}
|
||||
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
}
|
||||
|
||||
void pktbuf_release(const void *pkt)
|
||||
{
|
||||
_packet_t *packet = _pktbuf_head(), *prev = NULL;
|
||||
|
||||
mutex_lock(&_pktbuf_mutex);
|
||||
|
||||
while (packet != NULL) {
|
||||
|
||||
if ((_pkt_data(packet) <= pkt) && (pkt < _pkt_end(packet))) {
|
||||
_pktbuf_free(prev, packet);
|
||||
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
prev = packet;
|
||||
packet = (_packet_t *)packet->next;
|
||||
}
|
||||
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
}
|
||||
|
||||
#ifdef DEVELHELP
|
||||
#include <stdio.h>
|
||||
|
||||
void pktbuf_print(void)
|
||||
{
|
||||
_packet_t *packet = _pktbuf_head();
|
||||
int i = 0;
|
||||
|
||||
mutex_lock(&_pktbuf_mutex);
|
||||
|
||||
printf("current pktbuf allocations:\n");
|
||||
printf("===================================================\n");
|
||||
|
||||
if (packet->next == NULL && packet->size == 0) {
|
||||
printf("empty\n");
|
||||
printf("===================================================\n");
|
||||
printf("\n");
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
|
||||
return;
|
||||
}
|
||||
else if (packet->next != NULL && packet->size == 0) {
|
||||
packet = (_packet_t *)packet->next;
|
||||
}
|
||||
|
||||
while (packet != NULL) {
|
||||
uint8_t *data = (uint8_t *)_pkt_data(packet);
|
||||
|
||||
printf("packet %d (%p):\n", i, (void *)packet);
|
||||
printf(" next: %p\n", (void *)(packet->next));
|
||||
printf(" size: %" PRIu32 "\n", (uint32_t)packet->size);
|
||||
printf(" processing: %" PRIu8 "\n", packet->processing);
|
||||
|
||||
if (packet->next != NULL) {
|
||||
printf(" free data after: %" PRIu32 "\n",
|
||||
(uint32_t)(_pktbuf_start_idx((_packet_t *)(packet->next)) - _pktbuf_end_idx(packet) - 1));
|
||||
}
|
||||
else {
|
||||
printf(" free data after: %" PRIu32 "\n", (uint32_t)(PKTBUF_SIZE - _pktbuf_end_idx(packet) - 1));
|
||||
|
||||
}
|
||||
|
||||
printf(" data: (start address: %p)\n ", data);
|
||||
|
||||
if (packet->size > PKTBUF_SIZE) {
|
||||
printf(" We have a problem: packet->size (%" PRIu32 ") > PKTBUF_SIZE (%" PRIu32 ")\n",
|
||||
(uint32_t)(packet->size), (uint32_t)PKTBUF_SIZE);
|
||||
}
|
||||
else {
|
||||
for (size_t j = 0; j < packet->size; j++) {
|
||||
printf(" %02x", data[j]);
|
||||
|
||||
if (((j + 1) % 16) == 0) {
|
||||
printf("\n ");
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n\n");
|
||||
}
|
||||
|
||||
packet = (_packet_t *)packet->next;
|
||||
i++;
|
||||
}
|
||||
|
||||
printf("===================================================\n");
|
||||
printf("\n");
|
||||
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TEST_SUITES
|
||||
size_t pktbuf_bytes_allocated(void)
|
||||
{
|
||||
_packet_t *packet = _pktbuf_head();
|
||||
size_t bytes = 0;
|
||||
|
||||
mutex_lock(&_pktbuf_mutex);
|
||||
|
||||
while (packet != NULL) {
|
||||
bytes += packet->size;
|
||||
packet = (_packet_t *)(packet->next);
|
||||
}
|
||||
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
unsigned int pktbuf_packets_allocated(void)
|
||||
{
|
||||
_packet_t *packet = _pktbuf_head();
|
||||
unsigned int packets = 0;
|
||||
|
||||
mutex_lock(&_pktbuf_mutex);
|
||||
|
||||
while (packet != NULL) {
|
||||
if ((packet != _pktbuf_head()) || (packet->size > 0)) { /* ignore head if head->size == 0 */
|
||||
packets++;
|
||||
}
|
||||
|
||||
packet = (_packet_t *)(packet->next);
|
||||
}
|
||||
|
||||
mutex_unlock(&_pktbuf_mutex);
|
||||
|
||||
return packets;
|
||||
}
|
||||
|
||||
int pktbuf_is_empty(void)
|
||||
{
|
||||
return ((_pktbuf_head()->next == NULL) && (_pktbuf_head()->size == 0));
|
||||
}
|
||||
|
||||
void pktbuf_reset(void)
|
||||
{
|
||||
memset(_pktbuf, 0, PKTBUF_SIZE);
|
||||
mutex_init(&_pktbuf_mutex);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
@ -1,195 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup pktbuf Packet buffer
|
||||
* @ingroup net
|
||||
* @brief A global network packet buffer.
|
||||
* @{
|
||||
*
|
||||
* @file pktbuf.h
|
||||
* @brief Interface definition for the global network buffer. Network devices
|
||||
* and layers can allocate space for packets here.
|
||||
*
|
||||
* @note **WARNING!!** Do not store data structures that are not packed
|
||||
* (defined with `__attribute__((packed))`) or enforce alignment in
|
||||
* in any way in here. On some RISC architectures this *will* lead to
|
||||
* alignment problems and can potentially result in segmentation/hard
|
||||
* faults and other unexpected behaviour.
|
||||
*
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*/
|
||||
#ifndef __PKTBUF_H_
|
||||
#define __PKTBUF_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "cpu-conf.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef PKTBUF_SIZE
|
||||
/**
|
||||
* @brief Maximum size of the packet buffer.
|
||||
*
|
||||
* @details The rational here is to have at least space for 4 full-MTU IPv6
|
||||
* packages (2 incoming, 2 outgoing; 2 * 2 * 1280 B = 5 KiB) +
|
||||
* Meta-Data (roughly estimated to 1 KiB; might be smaller)
|
||||
*/
|
||||
#define PKTBUF_SIZE (6144)
|
||||
#endif /* PKTBUF_SIZE */
|
||||
|
||||
/**
|
||||
* @brief Allocates new packet data in the packet buffer. This also marks the
|
||||
* allocated data as processed.
|
||||
*
|
||||
* @see @ref pktbuf_hold()
|
||||
*
|
||||
* @param[in] size The length of the packet you want to allocate
|
||||
*
|
||||
* @return Pointer to the start of the data in the packet buffer, on success.
|
||||
* @return NULL, if no space is left in the packet buffer or size was 0.
|
||||
*/
|
||||
void *pktbuf_alloc(size_t size);
|
||||
|
||||
/**
|
||||
* @brief Reallocates new space in the packet buffer, without changing the
|
||||
* content.
|
||||
*
|
||||
* @details If enough memory is available behind it or *size* is smaller than
|
||||
* the original size the packet will not be moved. Otherwise, it will
|
||||
* be moved. If no space is available nothing happens.
|
||||
*
|
||||
* @param[in] pkt Old position of the packet in the packet buffer
|
||||
* @param[in] size New amount of data you want to allocate
|
||||
*
|
||||
* @return Pointer to the (maybe new) position of the packet in the packet buffer,
|
||||
* on success.
|
||||
* @return NULL, if no space is left in the packet buffer or size was 0.
|
||||
* The packet will remain at *ptr*.
|
||||
*/
|
||||
void *pktbuf_realloc(const void *pkt, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Allocates and copies new packet data into the packet buffer.
|
||||
* This also marks the allocated data as processed for the current
|
||||
* thread.
|
||||
*
|
||||
* @see @ref pktbuf_hold()
|
||||
*
|
||||
* @param[in] data Data you want to copy into the new packet.
|
||||
* @param[in] size The length of the packet you want to allocate
|
||||
*
|
||||
* @return Pointer to the start of the data in the packet buffer, on success.
|
||||
* @return NULL, if no space is left in the packet buffer.
|
||||
*/
|
||||
void *pktbuf_insert(const void *data, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Copies packet data into the packet buffer, safely.
|
||||
*
|
||||
* @details Use this instead of memcpy, since it is thread-safe and checks if
|
||||
* *pkt* is
|
||||
*
|
||||
* -# in the buffer at all
|
||||
* -# its *size* is smaller or equal to the data allocated at *pkt*
|
||||
*
|
||||
* If the *pkt* is not in the buffer the data is just copied as
|
||||
* memcpy would do.
|
||||
*
|
||||
* @param[in,out] pkt The packet you want to set the data for.
|
||||
* @param[in] data The data you want to copy into the packet.
|
||||
* @param[in] data_len The length of the data you want to copy.
|
||||
*
|
||||
* @return *data_len*, on success.
|
||||
* @return -EFAULT, if *data* is NULL and DEVELHELP is defined.
|
||||
* @return -EINVAL, if *pkt* is NULL and DEVELHELP is defined.
|
||||
* @return -ENOBUFS, if *data_len* was greater than the packet size of *pkt*.
|
||||
*/
|
||||
int pktbuf_copy(void *pkt, const void *data, size_t data_len);
|
||||
|
||||
/**
|
||||
* @brief Marks the data as being processed.
|
||||
*
|
||||
* @details Internally this increments just a counter on the data.
|
||||
* @ref pktbuf_release() decrements it. If the counter is <=0 the
|
||||
* reserved data block in the buffer will be made available again.
|
||||
*
|
||||
* @param[in] pkt The packet you want mark as being processed.
|
||||
*/
|
||||
void pktbuf_hold(const void *pkt);
|
||||
|
||||
/**
|
||||
* @brief Marks the data as not being processed.
|
||||
*
|
||||
* @param[in] pkt The packet you want mark as not being processed anymore.
|
||||
*
|
||||
* @details Internally this decrements just a counter on the data.
|
||||
* @ref pktbuf_hold() increments and any allocation
|
||||
* operation initializes it. If the counter is <=0 the reserved data
|
||||
* block in the buffer will be made available again.
|
||||
*/
|
||||
void pktbuf_release(const void *pkt);
|
||||
|
||||
/**
|
||||
* @brief Prints current packet buffer to stdout if DEVELHELP is defined.
|
||||
*/
|
||||
#ifdef DEVELHELP
|
||||
void pktbuf_print(void);
|
||||
#else
|
||||
#define pktbuf_print() ;
|
||||
#endif
|
||||
|
||||
/* for testing */
|
||||
#ifdef TEST_SUITES
|
||||
/**
|
||||
* @brief Counts the number of allocated bytes
|
||||
*
|
||||
* @return Number of allocated bytes
|
||||
*/
|
||||
size_t pktbuf_bytes_allocated(void);
|
||||
|
||||
/**
|
||||
* @brief Counts the number of allocated packets
|
||||
*
|
||||
* @return Number of allocated packets
|
||||
*/
|
||||
size_t pktbuf_packets_allocated(void);
|
||||
|
||||
/**
|
||||
* @brief Checks if packet buffer is empty
|
||||
*
|
||||
* @return 1, if packet buffer is empty
|
||||
* @return 0, if packet buffer is not empty
|
||||
*/
|
||||
int pktbuf_is_empty(void);
|
||||
|
||||
/**
|
||||
* @brief Checks if a given pointer is stored in the packet buffer
|
||||
*
|
||||
* @param[in] pkt Pointer to be checked
|
||||
*
|
||||
* @return 1, if *pkt* is in packet buffer
|
||||
* @return 0, otherwise
|
||||
*/
|
||||
int pktbuf_contains(const void *pkt);
|
||||
|
||||
/**
|
||||
* @brief Sets the whole packet buffer to 0
|
||||
*/
|
||||
void pktbuf_reset(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __PKTBUF_H_ */
|
||||
/** @} */
|
@ -7,6 +7,7 @@ BOARD_INSUFFICIENT_RAM := chronos msb-430 msb-430h redbee-econotag stm32f0discov
|
||||
USEMODULE += embunit
|
||||
|
||||
# Some randomly generated but still deterministic values for testing
|
||||
CFLAGS += -DTEST_STRING4="\"J&(d\""
|
||||
CFLAGS += -DTEST_STRING8="\"o<\\\\rrB/q\""
|
||||
CFLAGS += -DTEST_STRING12="\"50U'HLKC3_ft\""
|
||||
CFLAGS += -DTEST_STRING16="\"nvxuO*6o3C=a6g7]\""
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "tests-pkt.h"
|
||||
|
||||
#define _INIT_ELEM(len, data, next) \
|
||||
{ (next), (data), (len), NG_NETTYPE_UNDEF }
|
||||
{ 1, (next), (data), (len), NG_NETTYPE_UNDEF }
|
||||
#define _INIT_ELEM_STATIC_DATA(data, next) _INIT_ELEM(sizeof(data), data, next)
|
||||
|
||||
static void test_pkt_len__NULL(void)
|
||||
|
@ -1 +1 @@
|
||||
USEMODULE += pktbuf
|
||||
USEMODULE += ng_pktbuf
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user