Skip to content

Commit

Permalink
Merge pull request #2563 from authmillenon/ng_pktbuf/fix/semantics
Browse files Browse the repository at this point in the history
ng_pktbuf: change semantics for received packets
  • Loading branch information
haukepetersen committed Mar 24, 2015
2 parents 23d079b + 9ada601 commit 5f77bbe
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 124 deletions.
74 changes: 48 additions & 26 deletions sys/include/net/ng_pkt.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
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 @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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;

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 @@ -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;

Expand Down
Loading

0 comments on commit 5f77bbe

Please sign in to comment.