Skip to content

Commit

Permalink
ng_pktbuf: change semantics for received packets
Browse files Browse the repository at this point in the history
  • Loading branch information
miri64 committed Mar 23, 2015
1 parent 678ca8d commit 21204dc
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 96 deletions.
90 changes: 54 additions & 36 deletions sys/include/net/ng_pktbuf.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,38 +56,61 @@ extern "C" {
#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.
* @brief Adds a new ng_pktsnip_t and its packet to the packet buffer.
*
* @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:
*
* * 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:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Before After
* ====== =====
* (next)
* pkt->data result->data <== pkt->data
* v v v
* +--------------------------------+ +----------------+---------------+
* +--------------------------------+ +----------------+---------------+
* \__________pkt->size___________/ \_result->size_/ \__pkt->size__/
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @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,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);

/**
Expand All @@ -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
Expand All @@ -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
Expand Down
117 changes: 77 additions & 40 deletions sys/net/crosslayer/ng_pktbuf/ng_pktbuf.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,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)
Expand All @@ -87,16 +103,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)
Expand Down Expand Up @@ -147,60 +174,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;

if (size == 0) {
data = 0;
}
ng_pktsnip_t *new_pktsnip;

snip = (ng_pktsnip_t *)_pktbuf_internal_alloc(sizeof(ng_pktsnip_t));
new_pktsnip = (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;

LL_PREPEND(next, snip);
new_pktsnip->size = size;
new_pktsnip->type = type;
new_pktsnip->users = 1;

return snip;
return new_pktsnip;
}

static ng_pktsnip_t *_pktbuf_duplicate(const ng_pktsnip_t *pkt)
Expand All @@ -217,12 +254,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;

Expand Down
41 changes: 21 additions & 20 deletions tests/unittests/tests-pktbuf/tests-pktbuf.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,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);
Expand Down Expand Up @@ -312,8 +312,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());
Expand Down Expand Up @@ -458,31 +458,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_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(((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(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());
}

Expand Down

0 comments on commit 21204dc

Please sign in to comment.