1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2026-01-01 01:41:18 +01:00

Merge pull request #2563 from authmillenon/ng_pktbuf/fix/semantics

ng_pktbuf: change semantics for received packets
This commit is contained in:
Hauke Petersen 2015-03-24 16:49:49 +01:00
commit 5f77bbe1c8
4 changed files with 196 additions and 118 deletions

View File

@ -45,35 +45,57 @@ extern "C" {
* +---------------------------+ +------+
* | size = 14 | data +-------------->| |
* | type = NETTYPE_ETHERNET |------+ +------+
* +---------------------------+ . .
* | next . .
* v +------+
* +---------------------------+ +----------->| |
* | next | | |
* v | | |
* +---------------------------+ | +------+
* | size = 40 | data | +-------->| |
* | type = NETTYPE_IPV6 |---------+ | +------+
* +---------------------------+ | +----->| |
* | next | | +------+
* v | | +-->| |
* +---------------------------+ | | | | |
* | size = 8 | data | | | . .
* | type = NETTYPE_UDP |------------+ | | . .
* +---------------------------+ | |
* | next | |
* v | |
* +---------------------------+ | |
* | size = 5 | data | |
* | type = NETTYPE_COAP |---------------+ |
* +---------------------------+ |
* | next |
* v |
* +---------------------------+ |
* | size = 54 | data |
* | type = NETTYPE_UNKNOWN |------------------+
* +---------------------------+
* | size = 40 | data | | |
* | type = NETTYPE_IPV6 |---------+ +------+
* +---------------------------+ . .
* | next . .
* v +------+
* +---------------------------+ +-------->| |
* | size = 8 | data | +------+
* | type = NETTYPE_UDP |------------+ . .
* +---------------------------+ . .
* | next +------+
* v +----->| |
* +---------------------------+ | | |
* | size = 59 | data | . .
* | type = NETTYPE_UNDEF |---------------+ . .
* +---------------------------+ . .
*
* To keep data duplication as low as possible the order of the snips
* in a packet will be reversed depending on if you send the packet or if
* you received it. For sending the order is from (in the network stack) lowest
* protocol snip to the highest, for receiving the order is from highest
* snip to the lowest. This way, if a layer needs to duplicate the packet
* a tree is created rather than a duplication of the whole package.
*
* A very extreme example for this (we only expect one or two duplications at
* maximum per package) can be seen here:
*
* Sending Receiving
* ======= =========
*
* * Payload * L2 header
* ^ ^
* | |
* |\ |\
* | * L4 header 1 | * L2.5 header 1
* | * L3 header 1 | * L3 header 1
* | * netif header 1 | * L4 header 1
* * L4 header 2 | * Payload 1
* ^ * L3 header 2
* | ^
* |\ |
* | * L3 header 2 |\
* | * L2 header 2 | * L4 header 2
* * L2 header 3 | * Payload 2
* |\ * Payload 3
* | * L2 header 3
* * L2 header 4
*
* @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 net_ng_pktbuf
* as factory.
*/

View File

@ -56,38 +56,61 @@ extern "C" {
#endif /* NG_PKTBUF_SIZE */
/**
* @brief Prepends a new ng_pktsnip_t to a packet.
* @brief Adds a new ng_pktsnip_t and its packet to the packet buffer.
*
* @details It is ill-advised to add a ng_pktsnip_t simply by using
* @details This function is very powerful and reflects the unique characterics
* of ng_pktsnip_t of being reversed for either the sending or
* receiving context. Because of this the assumtion of the transmission
* direction, the state of the packet buffer and the values for the
* members of the resulting ng_pktsnip_t can be very different after
* execution of this function depending on what parameters you use:
*
* next = ng_pktsnip_add(NULL, NULL, size1, NG_NETTYPE_UNDEF);
* pkt = ng_pktsnip_add(NULL, NULL, size2, NG_NETTYPE_UNDEF);
* * for most cases the result will be pretty straight forward and the
* packet is either assumed to be in sending direction or for creation
* (@p pkt == NULL) there will be made no assumtions about direction at all.
* * if @p pkt != NULL, @p data = `pkt->data`, @p size < `pkt->size` receiving
* direction is assumed and the following values will be set:
* * ng_pktsnip_t::next of result = `pkt->next`
* * ng_pktsnip_t::data of result = @p data
* * ng_pktsnip_t::size of result = @p size
* * ng_pktsnip_t::next of @p pkt = result
* * ng_pktsnip_t::data of @p pkt = @p data + @p size
* * ng_pktsnip_t::size of @p pkt = old size value - @p size
* * graphically this can be represented as follows:
*
* pkt->next = next;
* next->data = next->data + size2;
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Before After
* ====== =====
* (next)
* pkt->data result->data <== pkt->data
* v v v
* +--------------------------------+ +----------------+---------------+
* +--------------------------------+ +----------------+---------------+
* \__________pkt->size___________/ \_result->size_/ \__pkt->size__/
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* 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.
* @note **Do not** change the ng_pktsnip_t::data and ng_pktsnip_t::size
* of a ng_pktsnip_t created by this function externally, except if
* they both are null or data is not from inside the packet buffer.
* This will most likely create memory leaks.
*
* @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.
* @param[in,out] pkt The packet you want to add a ng_pktsnip_t to. Leave
* NULL if you want to create a new packet. Members may
* change values; see above.
* @param[in] data Data of the new ng_pktsnip_t. If @p data is NULL no data
* will be inserted into the result. @p data that is already
* in the packet buffer (e.g. a payload of an already
* allocated packet) will not be duplicated.
* @param[in] size Length of @p data. If @p size is 0 no data will be inserted
* into the the packet buffer and ng_pktsnip_t::data will be
* set to @p data.
* @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.
* @return NULL, if @p pkt != NULL, @data = `pkt->data`, and @size > `pkt->data`.
*/
ng_pktsnip_t *ng_pktbuf_add(ng_pktsnip_t *next, void *data, size_t size,
ng_pktsnip_t *ng_pktbuf_add(ng_pktsnip_t *pkt, void *data, size_t size,
ng_nettype_t type);
/**
@ -101,8 +124,8 @@ ng_pktsnip_t *ng_pktbuf_add(ng_pktsnip_t *next, void *data, size_t size,
* 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.
* @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
@ -117,12 +140,7 @@ int ng_pktbuf_realloc_data(ng_pktsnip_t *pkt, size_t size);
* @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);
}
}
void ng_pktbuf_hold(ng_pktsnip_t *pkt, unsigned int num);
/**
* @brief Decreases ng_pktsnip_t::users of @p pkt atomically and removes it if it

View File

@ -66,18 +66,34 @@ int ng_pktbuf_realloc_data(ng_pktsnip_t *pkt, size_t size)
return 0;
}
ng_pktsnip_t *ng_pktbuf_add(ng_pktsnip_t *next, void *data, size_t size,
ng_pktsnip_t *ng_pktbuf_add(ng_pktsnip_t *pkt, void *data, size_t size,
ng_nettype_t type)
{
ng_pktsnip_t *snip;
ng_pktsnip_t *new_pktsnip;
mutex_lock(&_pktbuf_mutex);
snip = _pktbuf_add_unsafe(next, data, size, type);
new_pktsnip = _pktbuf_add_unsafe(pkt, data, size, type);
mutex_unlock(&_pktbuf_mutex);
return snip;
return new_pktsnip;
}
void ng_pktbuf_hold(ng_pktsnip_t *pkt, unsigned int num)
{
if ((pkt == NULL) || (num == 0)) {
return;
}
mutex_lock(&_pktbuf_mutex);
while (pkt != NULL) {
pkt->users += num;
pkt = pkt->next;
}
mutex_unlock(&_pktbuf_mutex);
}
void ng_pktbuf_release(ng_pktsnip_t *pkt)
@ -86,16 +102,27 @@ void ng_pktbuf_release(ng_pktsnip_t *pkt)
return;
}
atomic_set_return(&(pkt->users), pkt->users - 1);
mutex_lock(&_pktbuf_mutex);
if (pkt->users == 0 && _pktbuf_internal_contains(pkt->data)) {
mutex_lock(&_pktbuf_mutex);
while (pkt != NULL) {
if (pkt->users > 0) { /* Don't accidentally overshoot */
pkt->users--;
}
_pktbuf_internal_free(pkt->data);
_pktbuf_internal_free(pkt);
if (pkt->users == 0) {
if (_pktbuf_internal_contains(pkt->data)) {
_pktbuf_internal_free(pkt->data);
}
mutex_unlock(&_pktbuf_mutex);
if (_pktbuf_internal_contains(pkt)) {
_pktbuf_internal_free(pkt);
}
}
pkt = pkt->next;
}
mutex_unlock(&_pktbuf_mutex);
}
ng_pktsnip_t *ng_pktbuf_start_write(ng_pktsnip_t *pkt)
@ -146,60 +173,70 @@ static ng_pktsnip_t *_pktbuf_alloc(size_t size)
return pkt;
}
static ng_pktsnip_t *_pktbuf_add_unsafe(ng_pktsnip_t *next, void *data,
static ng_pktsnip_t *_pktbuf_add_unsafe(ng_pktsnip_t *pkt, void *data,
size_t size, ng_nettype_t type)
{
ng_pktsnip_t *snip;
ng_pktsnip_t *new_pktsnip;
if (size == 0) {
data = 0;
}
new_pktsnip = (ng_pktsnip_t *)_pktbuf_internal_alloc(sizeof(ng_pktsnip_t));
snip = (ng_pktsnip_t *)_pktbuf_internal_alloc(sizeof(ng_pktsnip_t));
if (snip == NULL) {
if (new_pktsnip == NULL) {
return NULL;
}
if (next == NULL || next->data != data) {
if (size != 0) {
snip->data = _pktbuf_internal_alloc(size);
if (pkt == NULL || pkt->data != data) {
if ((size != 0) && (!_pktbuf_internal_contains(data))) {
new_pktsnip->data = _pktbuf_internal_alloc(size);
if (snip->data == NULL) {
_pktbuf_internal_free(snip);
if (new_pktsnip->data == NULL) {
_pktbuf_internal_free(new_pktsnip);
return NULL;
}
if (data != NULL) {
memcpy(snip->data, data, size);
memcpy(new_pktsnip->data, data, size);
}
}
else {
snip->data = data;
if (_pktbuf_internal_contains(data)) {
if (!_pktbuf_internal_add_pkt(new_pktsnip->data)) {
_pktbuf_internal_free(new_pktsnip);
return NULL;
}
}
new_pktsnip->data = data;
}
new_pktsnip->next = NULL;
LL_PREPEND(pkt, new_pktsnip);
}
else {
snip->data = data;
if (size > pkt->size) {
return NULL;
}
next->size -= size;
next->data = (void *)(((uint8_t *)next->data) + size);
new_pktsnip->next = pkt->next;
new_pktsnip->data = data;
if (!_pktbuf_internal_add_pkt(next->data)) {
_pktbuf_internal_free(snip);
pkt->next = new_pktsnip;
pkt->size -= size;
pkt->data = (void *)(((uint8_t *)pkt->data) + size);
if (!_pktbuf_internal_add_pkt(pkt->data)) {
_pktbuf_internal_free(new_pktsnip);
return NULL;
}
}
snip->next = NULL;
snip->size = size;
snip->type = type;
snip->users = 1;
new_pktsnip->size = size;
new_pktsnip->type = type;
new_pktsnip->users = 1;
LL_PREPEND(next, snip);
return snip;
return new_pktsnip;
}
static ng_pktsnip_t *_pktbuf_duplicate(const ng_pktsnip_t *pkt)
@ -216,12 +253,12 @@ static ng_pktsnip_t *_pktbuf_duplicate(const ng_pktsnip_t *pkt)
res->type = pkt->type;
while (pkt->next) {
ng_pktsnip_t *header = NULL;
ng_pktsnip_t *hdr = NULL;
pkt = pkt->next;
header = _pktbuf_add_unsafe(res, pkt->data, pkt->size, pkt->type);
hdr = _pktbuf_add_unsafe(res, pkt->data, pkt->size, pkt->type);
if (header == NULL) {
if (hdr == NULL) {
do {
ng_pktsnip_t *next = res->next;

View File

@ -96,7 +96,7 @@ static void test_pktbuf_add__pkt_NULL__data_NOT_NULL__size_0(void)
TEST_ASSERT_NOT_NULL((pkt = ng_pktbuf_add(NULL, TEST_STRING8, 0, NG_NETTYPE_UNDEF)));
TEST_ASSERT_NULL(pkt->next);
TEST_ASSERT_NULL(pkt->data);
TEST_ASSERT_NOT_NULL(pkt->data);
TEST_ASSERT_EQUAL_INT(0, pkt->size);
TEST_ASSERT_EQUAL_INT(NG_NETTYPE_UNDEF, pkt->type);
@ -113,7 +113,7 @@ static void test_pktbuf_add__pkt_NOT_NULL__data_NOT_NULL__size_0(void)
TEST_ASSERT_NOT_NULL((pkt = ng_pktbuf_add(next, TEST_STRING8, 0, NG_NETTYPE_UNDEF)));
TEST_ASSERT(pkt->next == next);
TEST_ASSERT_NULL(pkt->data);
TEST_ASSERT_NOT_NULL(pkt->data);
TEST_ASSERT_EQUAL_INT(0, pkt->size);
TEST_ASSERT_EQUAL_INT(NG_NETTYPE_UNDEF, pkt->type);
@ -192,7 +192,7 @@ static void test_pktbuf_add__in_place(void)
ng_pktsnip_t *header;
TEST_ASSERT_NOT_NULL((header = ng_pktbuf_add(pkt, pkt->data, 4, NG_NETTYPE_UNDEF)));
TEST_ASSERT(header->next == pkt);
TEST_ASSERT(header == pkt->next);
TEST_ASSERT_EQUAL_STRING(TEST_STRING16, header->data); /* there is no 0 byte */
TEST_ASSERT_EQUAL_INT(4, header->size);
TEST_ASSERT_EQUAL_INT(NG_NETTYPE_UNDEF, header->type);
@ -313,8 +313,8 @@ static void test_pktbuf_realloc_data__pkt_users_gt_1(void)
static void test_pktbuf_realloc_data__pkt_next_neq_NULL(void)
{
ng_pktsnip_t *pkt = ng_pktbuf_add(NULL, NULL, sizeof(TEST_STRING8), NG_NETTYPE_UNDEF);
pkt->next = pkt;
TEST_ASSERT_NOT_NULL(ng_pktbuf_add(pkt, pkt->data, sizeof(TEST_STRING4), NG_NETTYPE_UNDEF));
TEST_ASSERT_EQUAL_INT(EINVAL, ng_pktbuf_realloc_data(pkt, sizeof(TEST_STRING8) - 1));
ng_pktbuf_release(pkt);
TEST_ASSERT(ng_pktbuf_is_empty());
@ -459,31 +459,32 @@ static void test_pktbuf_realloc_data__success2(void)
static void test_pktbuf_realloc_data__further_down_the_line(void)
{
ng_pktsnip_t *pkt, *header;
ng_pktsnip_t *pkt1, *pkt2, *header;
void *exp_data;
pkt = ng_pktbuf_add(NULL, TEST_STRING16, sizeof(TEST_STRING16), NG_NETTYPE_UNDEF);
exp_data = pkt->data;
pkt1 = ng_pktbuf_add(NULL, TEST_STRING16, sizeof(TEST_STRING16), NG_NETTYPE_UNDEF);
exp_data = pkt1->data;
TEST_ASSERT_NOT_NULL(pkt);
TEST_ASSERT_NOT_NULL(pkt1);
header = ng_pktbuf_add(pkt, pkt->data, 4, NG_NETTYPE_UNDEF);
header = ng_pktbuf_add(pkt1, pkt1->data, 4, NG_NETTYPE_UNDEF);
pkt2 = ng_pktbuf_add(NULL, TEST_STRING16, sizeof(TEST_STRING16), NG_NETTYPE_UNDEF);
TEST_ASSERT_NOT_NULL(header);
TEST_ASSERT(header->next == pkt);
TEST_ASSERT(header == pkt1->next);
TEST_ASSERT_EQUAL_INT(4, header->size);
TEST_ASSERT(((uint8_t *)pkt->data) == (((uint8_t *)header->data) + 4));
TEST_ASSERT_EQUAL_INT(sizeof(TEST_STRING16) - 4, pkt->size);
TEST_ASSERT(((uint8_t *)pkt1->data) == (((uint8_t *)header->data) + 4));
TEST_ASSERT_EQUAL_INT(sizeof(TEST_STRING16) - 4, pkt1->size);
TEST_ASSERT_EQUAL_INT(0, ng_pktbuf_realloc_data(pkt, 20));
TEST_ASSERT(exp_data != pkt->data);
TEST_ASSERT_NULL(pkt->next);
TEST_ASSERT_EQUAL_STRING(TEST_STRING16 + 4, pkt->data);
TEST_ASSERT_EQUAL_INT(20, pkt->size);
TEST_ASSERT_EQUAL_INT(NG_NETTYPE_UNDEF, pkt->type);
TEST_ASSERT_EQUAL_INT(1, pkt->users);
ng_pktbuf_release(pkt);
ng_pktbuf_release(header);
TEST_ASSERT_EQUAL_INT(0, ng_pktbuf_realloc_data(header, 40));
TEST_ASSERT(exp_data != header->data);
TEST_ASSERT_NULL(header->next);
TEST_ASSERT_EQUAL_STRING(TEST_STRING16, header->data);
TEST_ASSERT_EQUAL_INT(40, header->size);
TEST_ASSERT_EQUAL_INT(NG_NETTYPE_UNDEF, header->type);
TEST_ASSERT_EQUAL_INT(1, header->users);
ng_pktbuf_release(pkt1);
ng_pktbuf_release(pkt2);
TEST_ASSERT(ng_pktbuf_is_empty());
}