From 79a0d3d56952fbea42d96a118a53f839dceaa21f Mon Sep 17 00:00:00 2001 From: Rahix <rahix@rahix.de> Date: Fri, 3 Apr 2020 13:13:07 +0200 Subject: [PATCH] feat(interrupts): Dispatch interrupts asynchroneously Instead of blocking the triggering task when core 1 is still busy handling the previous interrupt, offload interrupt dispatching into a separate task. This is the first step towards making API-calls interrupt safe. Next to the async triggering, the synchroneous mechanism is retained for special cases where async does not work (e.g. because of spin-locks). Currently, there is only one such case when resetting core 1 (triggering EPIC_INT_RESET). Signed-off-by: Rahix <rahix@rahix.de> --- epicardium/api/control.c | 11 +++++-- epicardium/main.c | 10 ++++++ epicardium/modules/interrupts.c | 57 +++++++++++++++++++++++++++++++++ epicardium/modules/modules.h | 2 ++ 4 files changed, 77 insertions(+), 3 deletions(-) diff --git a/epicardium/api/control.c b/epicardium/api/control.c index f59978842..deff43301 100644 --- a/epicardium/api/control.c +++ b/epicardium/api/control.c @@ -9,7 +9,7 @@ #include "tmr.h" static void __core1_init(void); -extern void interrupt_trigger(api_int_id_t id); +extern void interrupt_trigger_sync(api_int_id_t id); struct core1_info { /* Location of core1's interrupt vector table */ @@ -206,8 +206,13 @@ void core1_boot(void) void core1_trigger_reset(void) { - /* Signal core 1 that we intend to load a new payload. */ - interrupt_trigger(EPIC_INT_RESET); + /* + * Signal core 1 that we intend to load a new payload. + * + * This needs to be synchroneous because otherwise we will deadlock + * (Lifecycle task busy-spins and interrupt can never get dispatched). + */ + interrupt_trigger_sync(EPIC_INT_RESET); } void core1_wait_ready(void) diff --git a/epicardium/main.c b/epicardium/main.c index c5e2768c3..89054e9e1 100644 --- a/epicardium/main.c +++ b/epicardium/main.c @@ -133,6 +133,16 @@ int main(void) &dispatcher_task_id) != pdPASS) { panic("Failed to create %s task!", "API Dispatcher"); } + /* Interrupts */ + if (xTaskCreate( + vInterruptsTask, + (const char *)"Interrupt Dispatcher", + configMINIMAL_STACK_SIZE, + NULL, + tskIDLE_PRIORITY + 2, + NULL) != pdPASS) { + panic("Failed to create %s task!", "Interrupt Dispatcher"); + } /* BLE */ if (ble_shall_start()) { diff --git a/epicardium/modules/interrupts.c b/epicardium/modules/interrupts.c index e33e35769..0ead54fc5 100644 --- a/epicardium/modules/interrupts.c +++ b/epicardium/modules/interrupts.c @@ -1,4 +1,5 @@ #include "modules/mutex.h" +#include "modules/log.h" #include "epicardium.h" #include "api/interrupt-sender.h" #include <assert.h> @@ -14,11 +15,28 @@ struct interrupt_priv { static struct interrupt_priv interrupt_data; static struct mutex interrupt_mutex; +static TaskHandle_t interrupts_task; void interrupt_trigger(api_int_id_t id) { assert(id < EPIC_INT_NUM); + mutex_lock(&interrupt_mutex); + + if (interrupt_data.int_enabled[id]) { + interrupt_data.int_pending[id] = true; + interrupt_data.has_pending = true; + mutex_unlock(&interrupt_mutex); + xTaskNotifyGive(interrupts_task); + } else { + mutex_unlock(&interrupt_mutex); + } +} + +void interrupt_trigger_sync(api_int_id_t id) +{ + assert(id < EPIC_INT_NUM); + mutex_lock(&interrupt_mutex); if (!interrupt_data.int_enabled[id]) goto out; @@ -97,3 +115,42 @@ int epic_interrupt_disable(api_int_id_t int_id) return 0; } /* }}} */ + +void vInterruptsTask(void *pvParameters) +{ + interrupts_task = xTaskGetCurrentTaskHandle(); + + while (true) { + mutex_lock(&interrupt_mutex); + + if (!interrupt_data.has_pending) { + /* Wait for a wakeup event from interrupt_trigger() */ + mutex_unlock(&interrupt_mutex); + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + mutex_lock(&interrupt_mutex); + } + + while (!api_interrupt_is_ready()) { + mutex_unlock(&interrupt_mutex); + vTaskDelay(pdMS_TO_TICKS(5)); + mutex_lock(&interrupt_mutex); + } + + api_int_id_t current_irq = EPIC_INT_NUM; + for (size_t i = 0; i < EPIC_INT_NUM; i++) { + if (interrupt_data.int_pending[i]) { + current_irq = i; + interrupt_data.int_pending[i] = false; + break; + } + } + + if (current_irq == EPIC_INT_NUM) { + interrupt_data.has_pending = false; + } else if (interrupt_data.int_enabled[current_irq]) { + api_interrupt_trigger(current_irq); + } + + mutex_unlock(&interrupt_mutex); + } +} diff --git a/epicardium/modules/modules.h b/epicardium/modules/modules.h index 534901c5c..c26a1ce6c 100644 --- a/epicardium/modules/modules.h +++ b/epicardium/modules/modules.h @@ -31,9 +31,11 @@ void return_to_menu(void); /* ---------- Interrupts --------------------------------------------------- */ void interrupt_init(void); void interrupt_trigger(api_int_id_t id); +void interrupt_trigger_sync(api_int_id_t id); void interrupt_trigger_unsafe(api_int_id_t id) __attribute__((deprecated( "interrupt_trigger_unsafe() is racy and only exists for legacy code." ))); +void vInterruptsTask(void *pvParameters); /* ---------- Serial ------------------------------------------------------- */ #define SERIAL_READ_BUFFER_SIZE 128 -- GitLab