2017-06-14 12:24:46 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2017 Freie Universität Berlin
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
|
|
|
*/
|
|
|
|
|
2021-01-22 18:00:34 +01:00
|
|
|
#include "mutex.h"
|
2017-06-14 12:24:46 +02:00
|
|
|
#include "net/gnrc/pktbuf.h"
|
sys/net/gnrc/tx_sync: new module
The new `gnrc_tx_sync` module allows users of the GNRC network stack to
synchronize with the actual transmission of outgoing packets. This is directly
integrated into gnrc_sock. Hence, if `gnrc_tx_sync` is used, calls to e.g.
sock_udp_send() will block until the network stack has processed the message.
Use cases:
1. Prevent packet drop when sending at high rate
- If the application is sending faster than the stack can handle, the
message queues will overflow and outgoing packets are lost
2. Passing auxiliary data about the transmission back the stack
- When e.g. the number of required retransmissions, the transmission time
stamp, etc. should be made available to a user of an UDP sock, a
synchronization mechanism is needed
3. Simpler error reporting without footguns
- The current approach of using `core/msg` for passing up error messages is
difficult to use if other message come in. Currently, gnrc_sock is
busy-waiting and fetching messages from the message queue until the number
of expected status reports is received. It will enqueue all
non-status-report messages again at the end of the queue. This has
multiple issues:
- Busy waiting is especially in lower power scenarios with time slotted
MAC protocols harmful, as the CPU will remain active and consume
power even though the it could sleep until the TX slot is reached
- The status reports from the network stack are send to the user thread
blocking. If the message queue of the user thread is full, the network
stack would block until the user stack can fetch the messages. If
another higher priority thread would start sending a message, it
would busy wait for its status reports to completely come in. Hence,
the first thread doesn't get CPU time to fetch messages and unblock
the network stack. As a result, the system would lock up completely.
- Just adding the error/status code to the gnrc_tx_sync_t would preallocate
and reserve memory for the error reporting. That way gnrc_sock does not
need to search through the message queue for status reports and the
network stack does not need to block for the user thread fetching it.
2020-12-23 15:46:59 +01:00
|
|
|
#include "net/gnrc/tx_sync.h"
|
2017-06-14 12:24:46 +02:00
|
|
|
|
2021-01-22 18:00:34 +01:00
|
|
|
#include "pktbuf_internal.h"
|
|
|
|
|
|
|
|
#define ENABLE_DEBUG 0
|
|
|
|
#include "debug.h"
|
|
|
|
|
|
|
|
mutex_t gnrc_pktbuf_mutex = MUTEX_INIT;
|
|
|
|
|
2017-06-14 12:24:46 +02:00
|
|
|
gnrc_pktsnip_t *gnrc_pktbuf_remove_snip(gnrc_pktsnip_t *pkt,
|
|
|
|
gnrc_pktsnip_t *snip)
|
|
|
|
{
|
2020-10-12 15:51:30 +02:00
|
|
|
pkt = gnrc_pkt_delete(pkt, snip);
|
2017-06-14 12:24:46 +02:00
|
|
|
snip->next = NULL;
|
|
|
|
gnrc_pktbuf_release(snip);
|
|
|
|
|
|
|
|
return pkt;
|
|
|
|
}
|
|
|
|
|
2018-10-22 21:08:36 +02:00
|
|
|
gnrc_pktsnip_t *gnrc_pktbuf_reverse_snips(gnrc_pktsnip_t *pkt)
|
|
|
|
{
|
|
|
|
gnrc_pktsnip_t *reversed = NULL, *ptr = pkt;
|
|
|
|
|
|
|
|
while (ptr != NULL) {
|
|
|
|
gnrc_pktsnip_t *next;
|
|
|
|
|
|
|
|
/* try to write-protect snip as its next-pointer is changed below */
|
|
|
|
pkt = gnrc_pktbuf_start_write(ptr); /* use pkt as temporary variable */
|
|
|
|
if (pkt == NULL) {
|
|
|
|
gnrc_pktbuf_release(reversed);
|
|
|
|
gnrc_pktbuf_release(ptr);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* switch around pointers */
|
|
|
|
next = pkt->next;
|
|
|
|
pkt->next = reversed;
|
|
|
|
reversed = pkt;
|
|
|
|
ptr = next;
|
|
|
|
}
|
|
|
|
return reversed;
|
|
|
|
}
|
|
|
|
|
2017-01-26 14:29:39 +01:00
|
|
|
int gnrc_pktbuf_merge(gnrc_pktsnip_t *pkt)
|
|
|
|
{
|
|
|
|
size_t offset = pkt->size;
|
|
|
|
size_t size = gnrc_pkt_len(pkt);
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
if (pkt->size == size) {
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Re-allocate data */
|
|
|
|
res = gnrc_pktbuf_realloc_data(pkt, size);
|
|
|
|
if (res != 0) {
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy data to new buffer */
|
|
|
|
for (gnrc_pktsnip_t *ptr = pkt->next; ptr != NULL; ptr = ptr->next) {
|
|
|
|
memcpy(((uint8_t *)pkt->data) + offset, ptr->data, ptr->size);
|
|
|
|
offset += ptr->size;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Release old pktsnips and data*/
|
|
|
|
gnrc_pktbuf_release(pkt->next);
|
|
|
|
pkt->next = NULL;
|
|
|
|
return res;
|
|
|
|
}
|
2017-06-14 12:24:46 +02:00
|
|
|
|
2021-01-22 18:00:34 +01:00
|
|
|
void gnrc_pktbuf_release_error(gnrc_pktsnip_t *pkt, uint32_t err)
|
|
|
|
{
|
|
|
|
mutex_lock(&gnrc_pktbuf_mutex);
|
|
|
|
while (pkt) {
|
|
|
|
gnrc_pktsnip_t *tmp;
|
|
|
|
assert(gnrc_pktbuf_contains(pkt));
|
|
|
|
assert(pkt->users > 0);
|
|
|
|
tmp = pkt->next;
|
|
|
|
if (pkt->users == 1) {
|
|
|
|
pkt->users = 0; /* not necessary but to be on the safe side */
|
sys/net/gnrc/tx_sync: new module
The new `gnrc_tx_sync` module allows users of the GNRC network stack to
synchronize with the actual transmission of outgoing packets. This is directly
integrated into gnrc_sock. Hence, if `gnrc_tx_sync` is used, calls to e.g.
sock_udp_send() will block until the network stack has processed the message.
Use cases:
1. Prevent packet drop when sending at high rate
- If the application is sending faster than the stack can handle, the
message queues will overflow and outgoing packets are lost
2. Passing auxiliary data about the transmission back the stack
- When e.g. the number of required retransmissions, the transmission time
stamp, etc. should be made available to a user of an UDP sock, a
synchronization mechanism is needed
3. Simpler error reporting without footguns
- The current approach of using `core/msg` for passing up error messages is
difficult to use if other message come in. Currently, gnrc_sock is
busy-waiting and fetching messages from the message queue until the number
of expected status reports is received. It will enqueue all
non-status-report messages again at the end of the queue. This has
multiple issues:
- Busy waiting is especially in lower power scenarios with time slotted
MAC protocols harmful, as the CPU will remain active and consume
power even though the it could sleep until the TX slot is reached
- The status reports from the network stack are send to the user thread
blocking. If the message queue of the user thread is full, the network
stack would block until the user stack can fetch the messages. If
another higher priority thread would start sending a message, it
would busy wait for its status reports to completely come in. Hence,
the first thread doesn't get CPU time to fetch messages and unblock
the network stack. As a result, the system would lock up completely.
- Just adding the error/status code to the gnrc_tx_sync_t would preallocate
and reserve memory for the error reporting. That way gnrc_sock does not
need to search through the message queue for status reports and the
network stack does not need to block for the user thread fetching it.
2020-12-23 15:46:59 +01:00
|
|
|
if (!IS_USED(MODULE_GNRC_TX_SYNC)
|
|
|
|
|| (pkt->type != GNRC_NETTYPE_TX_SYNC)) {
|
|
|
|
gnrc_pktbuf_free_internal(pkt->data, pkt->size);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
gnrc_tx_complete(pkt);
|
|
|
|
}
|
2021-01-22 18:00:34 +01:00
|
|
|
gnrc_pktbuf_free_internal(pkt, sizeof(gnrc_pktsnip_t));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
pkt->users--;
|
|
|
|
}
|
|
|
|
DEBUG("pktbuf: report status code %" PRIu32 "\n", err);
|
|
|
|
gnrc_neterr_report(pkt, err);
|
|
|
|
pkt = tmp;
|
|
|
|
}
|
|
|
|
mutex_unlock(&gnrc_pktbuf_mutex);
|
|
|
|
}
|
|
|
|
|
2017-06-14 12:24:46 +02:00
|
|
|
/** @} */
|