diff --git a/Makefile.dep b/Makefile.dep index dacaa1b387..e07d4c4f08 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -673,6 +673,10 @@ ifneq (,$(filter gnrc_netif_%,$(USEMODULE))) USEMODULE += gnrc_netif endif +ifneq (,$(filter gnrc_netif_pktq,$(USEMODULE))) + USEMODULE += xtimer +endif + ifneq (,$(filter netstats_%, $(USEMODULE))) USEMODULE += netstats endif diff --git a/sys/include/net/gnrc/netif.h b/sys/include/net/gnrc/netif.h index 63bb39fb52..fe04d658a9 100644 --- a/sys/include/net/gnrc/netif.h +++ b/sys/include/net/gnrc/netif.h @@ -59,6 +59,9 @@ #if IS_USED(MODULE_GNRC_NETIF_MAC) #include "net/gnrc/netif/mac.h" #endif +#ifdef MODULE_GNRC_NETIF_PKTQ +#include "net/gnrc/pktqueue.h" +#endif #include "net/ndp.h" #include "net/netdev.h" #include "net/netopt.h" @@ -67,6 +70,9 @@ #endif #include "rmutex.h" #include "net/netif.h" +#ifdef MODULE_GNRC_NETIF_PKTQ +#include "xtimer.h" +#endif #ifdef __cplusplus extern "C" { @@ -171,6 +177,16 @@ typedef struct { #endif #if IS_USED(MODULE_GNRC_NETIF_6LO) || defined(DOXYGEN) gnrc_netif_6lo_t sixlo; /**< 6Lo component */ +#endif +#if defined(MODULE_GNRC_NETIF_PKTQ) || DOXYGEN + /** + * @brief Packet queue for sending + * + * @note Only available with @ref net_gnrc_netif_pktq. + */ + gnrc_pktqueue_t *send_queue; + msg_t dequeue_msg; /**< message for gnrc_netif_t::dequeue_timer to send */ + xtimer_t dequeue_timer; /**< timer to schedule next sending of queued packets */ #endif uint8_t cur_hl; /**< Current hop-limit for out-going packets */ uint8_t device_type; /**< Device type */ diff --git a/sys/include/net/gnrc/netif/conf.h b/sys/include/net/gnrc/netif/conf.h index 7265d1ce09..58c73ff364 100644 --- a/sys/include/net/gnrc/netif/conf.h +++ b/sys/include/net/gnrc/netif/conf.h @@ -54,6 +54,30 @@ extern "C" { #define CONFIG_GNRC_NETIF_MSG_QUEUE_SIZE_EXP (4U) #endif +/** + * @brief Packet queue pool size for all network interfaces + * + * @note With @ref net_gnrc_sixlowpan_frag the queue should fit at least + * all fragments of the minimum MTU. + * @see net_gnrc_netif_pktq + */ +#ifndef CONFIG_GNRC_NETIF_PKTQ_POOL_SIZE +#define CONFIG_GNRC_NETIF_PKTQ_POOL_SIZE (16U) +#endif + +/** + * @brief Time in microseconds for when to try send a queued packet at the + * latest + * + * Set to -1 to deactivate dequeing by timer. For this it has to be ensured that + * none of the notifications by the driver are missed! + * + * @see net_gnrc_netif_pktq + */ +#ifndef CONFIG_GNRC_NETIF_PKTQ_TIMER_US +#define CONFIG_GNRC_NETIF_PKTQ_TIMER_US (5000U) +#endif + /** * @brief Number of multicast addresses needed for @ref net_gnrc_rpl "RPL". * diff --git a/sys/include/net/gnrc/netif/internal.h b/sys/include/net/gnrc/netif/internal.h index 07ae858f2c..e69665a594 100644 --- a/sys/include/net/gnrc/netif/internal.h +++ b/sys/include/net/gnrc/netif/internal.h @@ -35,10 +35,15 @@ extern "C" { #endif +/** + * @brief Message type to send from @ref net_gnrc_netif_pktq + */ +#define GNRC_NETIF_PKTQ_DEQUEUE_MSG (0x1233) + /** * @brief Message type for @ref netdev_event_t "netdev events" */ -#define NETDEV_MSG_TYPE_EVENT (0x1234) +#define NETDEV_MSG_TYPE_EVENT (0x1234) /** * @brief Acquires exclusive access to the interface diff --git a/sys/include/net/gnrc/netif/pktq.h b/sys/include/net/gnrc/netif/pktq.h new file mode 100644 index 0000000000..582f83757a --- /dev/null +++ b/sys/include/net/gnrc/netif/pktq.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2019-20 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. + */ + +/** + * @defgroup net_gnrc_netif_pktq Send queue for @ref net_gnrc_netif + * @ingroup net_gnrc_netif + * @brief + * @{ + * + * @file + * @brief @ref net_gnrc_netif_pktq definitions + * + * @author Martine S. Lenders + */ +#ifndef NET_GNRC_NETIF_PKTQ_H +#define NET_GNRC_NETIF_PKTQ_H + +#include +#include + +#include "net/gnrc/netif.h" +#include "net/gnrc/netif/pktq/type.h" +#include "net/gnrc/pkt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Puts a packet into the packet send queue of a network interface + * + * @pre `netif != NULL` + * @pre `pkt != NULL` + * + * @param[in] netif A network interface. May not be NULL. + * @param[in] pkt A packet. May not be NULL. + * + * @return 0 on success + * @return -1 when the pool of available gnrc_pktqueue_t entries (of size + * @ref CONFIG_GNRC_NETIF_PKTQ_POOL_SIZE) is depleted + */ +int gnrc_netif_pktq_put(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt); + +/** + * @brief Gets a packet from the packet send queue of a network interface + * + * @pre `netif != NULL` + * + * @param[in] netif A network interface. May not be NULL. + * + * @return A packet on success + * @return NULL when the queue is empty + */ +static inline gnrc_pktsnip_t *gnrc_netif_pktq_get(gnrc_netif_t *netif) +{ +#if IS_USED(MODULE_GNRC_NETIF_PKTQ) + assert(netif != NULL); + + gnrc_pktsnip_t *pkt = NULL; + gnrc_pktqueue_t *entry = gnrc_pktqueue_remove_head( + &netif->send_queue.queue + ); + + if (entry != NULL) { + pkt = entry->pkt; + entry->pkt = NULL; + } + return pkt; +#else /* IS_USED(MODULE_GNRC_NETIF_PKTQ) */ + (void)netif; + return NULL; +#endif /* IS_USED(MODULE_GNRC_NETIF_PKTQ) */ +} + +/** + * @brief Schedule a dequeue notification to network interface + * + * The notification will be scheduled in @ref CONFIG_GNRC_NETIF_PKTQ_TIMER_US + * microseconds. + * + * @pre `netif != NULL` + * + * The signaling message can be used to send the next message in + * gnrc_netif_pktq_t::queue. + * + * @param[in] netif A network interface. May not be NULL. + */ +void gnrc_netif_pktq_sched_get(gnrc_netif_t *netif); + +/** + * @brief Pushes a packet back to the head of the packet send queue of a + * network interface + * + * @pre `netif != NULL` + * @pre `pkt != NULL` + * + * @param[in] netif A network interface. May not be NULL. + * @param[in] pkt A packet. May not be NULL. + * + * @return 0 on success + * @return -1 when the pool of available gnrc_pktqueue_t entries (of size + * @ref CONFIG_GNRC_NETIF_PKTQ_POOL_SIZE) is depleted + */ +int gnrc_netif_pktq_push_back(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt); + +/** + * @brief Check if a network interface's packet send queue is empty + * + * @pre `netif != NULL` + * + * @param[in] netif A network interface. May not be NULL. + * + * @return true, when the packet send queue of @p netif is empty + * @return false, otherwise + */ +static inline bool gnrc_netif_pktq_empty(gnrc_netif_t *netif) +{ +#if IS_USED(MODULE_GNRC_NETIF_PKTQ) + assert(netif != NULL); + + return (netif->send_queue.queue == NULL); +#else /* IS_USED(MODULE_GNRC_NETIF_PKTQ) */ + (void)netif; + return false; +#endif /* IS_USED(MODULE_GNRC_NETIF_PKTQ) */ +} + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_NETIF_PKTQ_H */ +/** @} */ diff --git a/sys/include/net/gnrc/netif/pktq/type.h b/sys/include/net/gnrc/netif/pktq/type.h new file mode 100644 index 0000000000..95823b78d0 --- /dev/null +++ b/sys/include/net/gnrc/netif/pktq/type.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019-20 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. + */ + +/** + * @addtogroup net_gnrc_netif_pktq + * @brief + * @{ + * + * @file + * @brief @ref net_gnrc_netif_pktq type definitions + * + * Contained in its own file, so the type can be included in + * @ref gnrc_netif_t while the functions in net/gnrc/netif/pktq.h can use + * @ref gnrc_netif_t as operating type. + * + * @author Martine S. Lenders + */ +#ifndef NET_GNRC_NETIF_PKTQ_TYPE_H +#define NET_GNRC_NETIF_PKTQ_TYPE_H + +#include "net/gnrc/pktqueue.h" +#include "xtimer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief A packet queue for @ref net_gnrc_netif with a de-queue timer + */ +typedef struct { + gnrc_pktqueue_t *queue; /**< the actual packet queue class */ +#if CONFIG_GNRC_NETIF_PKTQ_TIMER_US >= 0 + msg_t dequeue_msg; /**< message for gnrc_netif_pktq_t::dequeue_timer to send */ + xtimer_t dequeue_timer; /**< timer to schedule next sending of + * queued packets */ +#endif +} gnrc_netif_pktq_t; + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_NETIF_PKTQ_TYPE_H */ +/** @} */ diff --git a/sys/net/gnrc/netif/Kconfig b/sys/net/gnrc/netif/Kconfig index c1aa3a4928..0592ccdab0 100644 --- a/sys/net/gnrc/netif/Kconfig +++ b/sys/net/gnrc/netif/Kconfig @@ -48,4 +48,17 @@ config GNRC_NETIF_NONSTANDARD_6LO_MTU This is non compliant with RFC 4944 and might not be supported by other implementations. +config GNRC_NETIF_PKTQ_POOL_SIZE + int "Packet queue pool size for all network interfaces" + depends on USEMODULE_GNRC_NETIF_PKTQ + default 16 + +config GNRC_NETIF_PKTQ_TIMER_US + int "Time in microseconds for when to try to send a queued packet at the latest" + depends on USEMODULE_GNRC_NETIF_PKTQ + default 5000 + help + Set to -1 to deactivate dequeing by timer. For this it has to be ensured + that none of the notifications by the driver are missed! + endif # KCONFIG_USEMODULE_GNRC_NETIF diff --git a/sys/net/gnrc/netif/Makefile b/sys/net/gnrc/netif/Makefile index ca2ce0eb12..bff0b18138 100644 --- a/sys/net/gnrc/netif/Makefile +++ b/sys/net/gnrc/netif/Makefile @@ -9,6 +9,9 @@ endif ifneq (,$(filter gnrc_netif_init_devs,$(USEMODULE))) DIRS += init_devs endif +ifneq (,$(filter gnrc_netif_pktq,$(USEMODULE))) + DIRS += pktq +endif ifneq (,$(filter gnrc_netif_hdr,$(USEMODULE))) DIRS += hdr endif diff --git a/sys/net/gnrc/netif/gnrc_netif.c b/sys/net/gnrc/netif/gnrc_netif.c index 5a78d089a3..7b0c0055b3 100644 --- a/sys/net/gnrc/netif/gnrc_netif.c +++ b/sys/net/gnrc/netif/gnrc_netif.c @@ -1161,7 +1161,7 @@ static void _configure_netdev(netdev_t *dev) if (res < 0) { DEBUG("gnrc_netif: enable NETOPT_RX_END_IRQ failed: %d\n", res); } -#ifdef MODULE_NETSTATS_L2 +#if defined(MODULE_NETSTATS_L2) || defined(MODULE_GNRC_NETIF_PKTQ) res = dev->driver->set(dev, NETOPT_TX_END_IRQ, &enable, sizeof(enable)); if (res < 0) { DEBUG("gnrc_netif: enable NETOPT_TX_END_IRQ failed: %d\n", res); @@ -1292,6 +1292,8 @@ void gnrc_netif_default_init(gnrc_netif_t *netif) #endif } +static void _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt, bool requeue); + #if IS_USED(MODULE_GNRC_NETIF_EVENTS) /** * @brief Call the ISR handler from an event @@ -1547,10 +1549,21 @@ static void _event_cb(netdev_t *dev, netdev_event_t event) switch (event) { case NETDEV_EVENT_RX_COMPLETE: pkt = netif->ops->recv(netif); + /* send packet previously queued within netif due to the lower + * layer being busy. + * Further packets will be sent on later TX_COMPLETE */ + _send_queued_pkt(netif); if (pkt) { _pass_on_packet(pkt); } break; +#if defined(MODULE_NETSTATS_L2) || defined(MODULE_GNRC_NETIF_PKTQ) + case NETDEV_EVENT_TX_COMPLETE: + /* send packet previously queued within netif due to the lower + * layer being busy. + * Further packets will be sent on later TX_COMPLETE or + * TX_MEDIUM_BUSY */ + _send_queued_pkt(netif); #ifdef MODULE_NETSTATS_L2 case NETDEV_EVENT_TX_MEDIUM_BUSY: /* we are the only ones supposed to touch this variable, diff --git a/sys/net/gnrc/netif/pktq/Makefile b/sys/net/gnrc/netif/pktq/Makefile new file mode 100644 index 0000000000..79b24af463 --- /dev/null +++ b/sys/net/gnrc/netif/pktq/Makefile @@ -0,0 +1,3 @@ +MODULE := gnrc_netif_pktq + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/netif/pktq/gnrc_netif_pktq.c b/sys/net/gnrc/netif/pktq/gnrc_netif_pktq.c new file mode 100644 index 0000000000..a1e32c602d --- /dev/null +++ b/sys/net/gnrc/netif/pktq/gnrc_netif_pktq.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 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 + */ + +#include "net/gnrc/pktqueue.h" +#include "net/gnrc/netif/conf.h" +#include "net/gnrc/netif/internal.h" +#include "net/gnrc/netif/pktq.h" + +static gnrc_pktqueue_t _pool[CONFIG_GNRC_NETIF_PKTQ_POOL_SIZE]; + +static gnrc_pktqueue_t *_get_free_entry(void) +{ + for (unsigned i = 0; i < CONFIG_GNRC_NETIF_PKTQ_POOL_SIZE; i++) { + if (_pool[i].pkt == NULL) { + return &_pool[i]; + } + } + return NULL; +} + +int gnrc_netif_pktq_put(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt) +{ + assert(netif != NULL); + assert(pkt != NULL); + + gnrc_pktqueue_t *entry = _get_free_entry(); + + if (entry == NULL) { + return -1; + } + entry->pkt = pkt; + gnrc_pktqueue_add(&netif->send_queue.queue, entry); + return 0; +} + +void gnrc_netif_pktq_sched_get(gnrc_netif_t *netif) +{ +#if CONFIG_GNRC_NETIF_PKTQ_TIMER_US >= 0 + assert(netif != NULL); + netif->send_queue.dequeue_msg.type = GNRC_NETIF_PKTQ_DEQUEUE_MSG; + /* Prevent timer from firing while we add this. + * Otherwise the system might crash: The timer handler sets + * netif->send_queue.dequeue_msg.sender_pid to KERNEL_PID_ISR while + * the message is added to the timer, causing the next round of the timer + * handler to try to send the message to IPC, leaving the system in an + * invalid state. */ + unsigned state = irq_disable(); + xtimer_set_msg(&netif->send_queue.dequeue_timer, + CONFIG_GNRC_NETIF_PKTQ_TIMER_US, + &netif->send_queue.dequeue_msg, netif->pid); + irq_restore(state); +#else /* CONFIG_GNRC_NETIF_PKTQ_TIMER_US >= 0 */ + (void)netif; +#endif /* CONFIG_GNRC_NETIF_PKTQ_TIMER_US >= 0 */ +} + +int gnrc_netif_pktq_push_back(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt) +{ + assert(netif != NULL); + assert(pkt != NULL); + + gnrc_pktqueue_t *entry = _get_free_entry(); + + if (entry == NULL) { + return -1; + } + entry->pkt = pkt; + LL_PREPEND(netif->send_queue.queue, entry); + return 0; +} + +/** @} */