From 2013ac75f553eed3a877d24d3cc72f67ab56794b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Mon, 11 Jun 2018 16:07:11 +0200 Subject: [PATCH] gnrc_netif: Add support for internal event loop Enabled by the gnrc_netif_events pseudo module. Using an internal event loop within the gnrc_netif thread eliminates the risk of lost interrupts and lets ISR events always be handled before any send/receive requests from other threads are processed. The events in the event loop is also a potential hook for MAC layers and other link layer modules which may need to inject and process events before any external IPC messages are handled. Co-Authored-By: Koen Zandberg --- Makefile.dep | 5 ++ makefiles/pseudomodules.inc.mk | 1 + sys/include/net/gnrc/netif.h | 11 +++ sys/net/gnrc/netif/gnrc_netif.c | 128 ++++++++++++++++++++++++++++++-- 4 files changed, 138 insertions(+), 7 deletions(-) diff --git a/Makefile.dep b/Makefile.dep index a850147b09..f8f9fb4f3b 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -195,6 +195,11 @@ ifneq (,$(filter gnrc_netif,$(USEMODULE))) endif endif +ifneq (,$(filter gnrc_netif_events,$(USEMODULE))) + USEMODULE += core_thread_flags + USEMODULE += event +endif + ifneq (,$(filter ieee802154 nrfmin esp_now cc110x gnrc_sixloenc,$(USEMODULE))) ifneq (,$(filter gnrc_ipv6, $(USEMODULE))) USEMODULE += gnrc_sixlowpan diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 0abd042378..aee9f3f23d 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -31,6 +31,7 @@ PSEUDOMODULES += gnrc_netdev_default PSEUDOMODULES += gnrc_neterr PSEUDOMODULES += gnrc_netapi_callbacks PSEUDOMODULES += gnrc_netapi_mbox +PSEUDOMODULES += gnrc_netif_events PSEUDOMODULES += gnrc_pktbuf_cmd PSEUDOMODULES += gnrc_netif_cmd_% PSEUDOMODULES += gnrc_netif_dedup diff --git a/sys/include/net/gnrc/netif.h b/sys/include/net/gnrc/netif.h index fff5b18ac6..a8f19e3821 100644 --- a/sys/include/net/gnrc/netif.h +++ b/sys/include/net/gnrc/netif.h @@ -30,6 +30,7 @@ #include "kernel_types.h" #include "msg.h" +#include "event.h" #include "net/ipv6/addr.h" #include "net/gnrc/netapi.h" #include "net/gnrc/pkt.h" @@ -94,6 +95,16 @@ typedef struct { * @see net_gnrc_netif_flags */ uint32_t flags; +#if IS_USED(MODULE_GNRC_NETIF_EVENTS) || IS_ACTIVE(DOXYGEN) + /** + * @brief Event queue for asynchronous events + */ + event_queue_t evq; + /** + * @brief ISR event for the network device + */ + event_t event_isr; +#endif /* MODULE_GNRC_NETIF_EVENTS */ #if (GNRC_NETIF_L2ADDR_MAXLEN > 0) || DOXYGEN /** * @brief The link-layer address currently used as the source address diff --git a/sys/net/gnrc/netif/gnrc_netif.c b/sys/net/gnrc/netif/gnrc_netif.c index 428c1e2711..6cdaad5e85 100644 --- a/sys/net/gnrc/netif/gnrc_netif.c +++ b/sys/net/gnrc/netif/gnrc_netif.c @@ -19,6 +19,7 @@ #include #include "bitfield.h" +#include "event.h" #include "net/ethernet.h" #include "net/ipv6.h" #include "net/gnrc.h" @@ -40,6 +41,16 @@ #define ENABLE_DEBUG (0) #include "debug.h" +#ifdef MODULE_GNRC_NETIF_EVENTS +/** + * @brief Event type used for passing netdev pointers together with the event + */ +typedef struct { + event_t super; + netdev_t *dev; +} event_netdev_t; +#endif /* MODULE_GNRC_NETIF_EVENTS */ + static void _update_l2addr_from_dev(gnrc_netif_t *netif); static void _configure_netdev(netdev_t *dev); static void *_gnrc_netif_thread(void *args); @@ -1348,6 +1359,93 @@ void gnrc_netif_default_init(gnrc_netif_t *netif) #endif } +#if IS_USED(MODULE_GNRC_NETIF_EVENTS) +/** + * @brief Call the ISR handler from an event + * + * @param[in] evp pointer to the event + */ +static void _event_handler_isr(event_t *evp) +{ + gnrc_netif_t *netif = container_of(evp, gnrc_netif_t, event_isr); + netif->dev->driver->isr(netif->dev); +} +#endif + +static inline void _event_post(gnrc_netif_t *netif) +{ +#if IS_USED(MODULE_GNRC_NETIF_EVENTS) + event_post(&netif->evq, &netif->event_isr); +#else + (void)netif; +#endif +} + +/** + * @brief Retrieve the netif event queue if enabled + * + * @param[in] netif gnrc_netif instance to operate on + * + * @return NULL if MODULE_GNRC_NETIF_EVENTS is not enabled + * @return gnrc_netif_t::evq if MODULE_GNRC_NETIF_EVENTS is enabled + */ +static inline event_queue_t *_get_evq(gnrc_netif_t *netif) +{ +#ifdef MODULE_GNRC_NETIF_EVENTS + return &netif->evq; +#else + (void)netif; + return NULL; +#endif +} + +/** + * @brief Process any pending events and wait for IPC messages + * + * This function will block until an IPC message is received. Events posted to + * the event queue will be processed while waiting for messages. + * + * @param[in] netif gnrc_netif instance to operate on + * @param[out] msg pointer to message buffer to write the first received message + * + * @return >0 if msg contains a new message + */ +static void _process_events_await_msg(gnrc_netif_t *netif, msg_t *msg) +{ + if (IS_USED(MODULE_GNRC_NETIF_EVENTS)) { + while (1) { + /* Using messages for external IPC, and events for internal events */ + + /* First drain the queues before blocking the thread */ + /* Events will be handled before messages */ + DEBUG("gnrc_netif: handling events\n"); + event_t *evp; + /* We can not use event_loop() or event_wait() because then we would not + * wake up when a message arrives */ + event_queue_t *evq = _get_evq(netif); + while ((evp = event_get(evq))) { + DEBUG("gnrc_netif: event %p\n", (void *)evp); + if (evp->handler) { + evp->handler(evp); + } + } + /* non-blocking msg check */ + int msg_waiting = msg_try_receive(msg); + if (msg_waiting > 0) { + return; + } + DEBUG("gnrc_netif: waiting for events\n"); + /* Block the thread until something interesting happens */ + thread_flags_wait_any(THREAD_FLAG_MSG_WAITING | THREAD_FLAG_EVENT); + } + } + else { + /* Only messages used for event handling */ + DEBUG("gnrc_netif: waiting for incoming messages\n"); + msg_receive(msg); + } +} + static void *_gnrc_netif_thread(void *args) { gnrc_netapi_opt_t *opt; @@ -1355,13 +1453,20 @@ static void *_gnrc_netif_thread(void *args) netdev_t *dev; int res; msg_t reply = { .type = GNRC_NETAPI_MSG_TYPE_ACK }; - msg_t msg, msg_queue[CONFIG_GNRC_NETIF_MSG_QUEUE_SIZE]; + msg_t msg_queue[CONFIG_GNRC_NETIF_MSG_QUEUE_SIZE]; DEBUG("gnrc_netif: starting thread %i\n", sched_active_pid); netif = args; gnrc_netif_acquire(netif); dev = netif->dev; netif->pid = sched_active_pid; + +#if IS_USED(MODULE_GNRC_NETIF_EVENTS) + netif->event_isr.handler = _event_handler_isr, + /* set up the event queue */ + event_queue_init(&netif->evq); +#endif /* MODULE_GNRC_NETIF_EVENTS */ + /* setup the link-layer's message queue */ msg_init_queue(msg_queue, CONFIG_GNRC_NETIF_MSG_QUEUE_SIZE); /* register the event callback with the device driver */ @@ -1394,9 +1499,13 @@ static void *_gnrc_netif_thread(void *args) #endif while (1) { - DEBUG("gnrc_netif: waiting for incoming messages\n"); - msg_receive(&msg); + msg_t msg; + /* msg will be filled by _process_events_await_msg. + * The function will not return until a message has been received. */ + _process_events_await_msg(netif, &msg); + /* dispatch netdev, MAC and gnrc_netapi messages */ + DEBUG("gnrc_netif: message %u\n", (unsigned)msg.type); switch (msg.type) { case NETDEV_MSG_TYPE_EVENT: DEBUG("gnrc_netif: GNRC_NETDEV_MSG_TYPE_EVENT received\n"); @@ -1487,11 +1596,16 @@ static void _event_cb(netdev_t *dev, netdev_event_t event) gnrc_netif_t *netif = (gnrc_netif_t *) dev->context; if (event == NETDEV_EVENT_ISR) { - msg_t msg = { .type = NETDEV_MSG_TYPE_EVENT, - .content = { .ptr = netif } }; + if (IS_USED(MODULE_GNRC_NETIF_EVENTS)) { + _event_post(netif); + } + else { + msg_t msg = { .type = NETDEV_MSG_TYPE_EVENT, + .content = { .ptr = netif } }; - if (msg_send(&msg, netif->pid) <= 0) { - puts("gnrc_netif: possibly lost interrupt."); + if (msg_send(&msg, netif->pid) <= 0) { + puts("gnrc_netif: possibly lost interrupt."); + } } } else {