From 980ffe3b4e41ce5e43c3e8ae13f605f7d7b50b7f Mon Sep 17 00:00:00 2001 From: Jerzy Kasenberg Date: Fri, 14 Jan 2022 09:14:49 +0100 Subject: [PATCH] nrf5x: Fix DMA access race condition In multi-thread mode starting DMA in thread mode was prone to race condition resulting in infinite loop. It may happen on single core CPU with strict priority based tasks scheduler where ready high prio task never yields to ready low prio task (Mynewt). Sequence that failed (T1 - low priority task, T2 - high priority task) - T1 called start_dma() - T1 set _dcd.dma_running (DMA not started yet, context switch happens) - T2 took CPU and saw that _dcd.dma_running is set, so waits for _dcd.dma_running to be 0 - T1 never gets CPU again, DMA is not started T2 waits forever OSAL mutex resolves problem of DMA starting from thread-context. --- src/portable/nordic/nrf5x/dcd_nrf5x.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c index 6b2cd83fd..e70565a02 100644 --- a/src/portable/nordic/nrf5x/dcd_nrf5x.c +++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c @@ -79,6 +79,9 @@ typedef struct } xfer_td_t; +static osal_mutex_def_t dcd_mutex_def; +static osal_mutex_t dcd_mutex; + // Data for managing dcd static struct { @@ -154,6 +157,7 @@ static void edpt_dma_start(volatile uint32_t* reg_startep) // Should be safe to blocking wait until previous DMA transfer complete uint8_t const rhport = 0; bool started = false; + osal_mutex_lock(dcd_mutex, OSAL_TIMEOUT_WAIT_FOREVER); while(!started) { // LDREX/STREX may be needed in form of std atomic (required C11) or @@ -170,6 +174,7 @@ static void edpt_dma_start(volatile uint32_t* reg_startep) // osal_yield(); } + osal_mutex_unlock(dcd_mutex); } } @@ -243,6 +248,7 @@ static void xact_in_dma(uint8_t epnum) void dcd_init (uint8_t rhport) { TU_LOG1("dcd init\r\n"); + dcd_mutex = osal_mutex_create(&dcd_mutex_def); (void) rhport; }