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.
This commit is contained in:
Jerzy Kasenberg 2022-01-14 09:14:49 +01:00
parent 0b6b4f2441
commit 980ffe3b4e
1 changed files with 6 additions and 0 deletions

View File

@ -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;
}