HAL_ChibiOS: fixed a race condition in UART DMA transmit

this fixes an issue seen on one board which caused a watchdog on high
uart DMA load. We have reproduced the issue on another board by
forcing a very high DMA transfer rate on the same DMA channel while
also requesting very high transfer rates on the UART. The likely race
is in the DMA transmit timeout code, and the simplest fix is to lock
out interrupts during the DMA setup to ensure the tx timeout cannot
trigger during the setup
This commit is contained in:
Andrew Tridgell 2020-10-07 17:41:13 +11:00
parent 9bde294112
commit 989f8c5d41
2 changed files with 7 additions and 4 deletions

View File

@ -802,7 +802,8 @@ void UARTDriver::handle_tx_timeout(void *arg)
chSysLockFromISR();
if (!uart_drv->tx_bounce_buf_ready) {
dmaStreamDisable(uart_drv->txdma);
uart_drv->tx_len -= dmaStreamGetTransactionSize(uart_drv->txdma);
const uint32_t tx_size = dmaStreamGetTransactionSize(uart_drv->txdma);
uart_drv->tx_len -= MIN(uart_drv->tx_len, tx_size);
uart_drv->tx_bounce_buf_ready = true;
uart_drv->dma_handle->unlock_from_IRQ();
}
@ -845,6 +846,7 @@ void UARTDriver::write_pending_bytes_DMA(uint32_t n)
}
}
chSysLock();
dmaStreamDisable(txdma);
tx_bounce_buf_ready = false;
osalDbgAssert(txdma != nullptr, "UART TX DMA allocation failed");
@ -858,7 +860,8 @@ void UARTDriver::write_pending_bytes_DMA(uint32_t n)
STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE);
dmaStreamEnable(txdma);
uint32_t timeout_us = ((1000000UL * (tx_len+2) * 10) / _baudrate) + 500;
chVTSet(&tx_timeout, chTimeUS2I(timeout_us), handle_tx_timeout, this);
chVTSetI(&tx_timeout, chTimeUS2I(timeout_us), handle_tx_timeout, this);
chSysUnlock();
}
#endif // HAL_UART_NODMA

View File

@ -141,7 +141,7 @@ private:
uint32_t lock_read_key;
uint32_t _baudrate;
uint16_t tx_len;
volatile uint16_t tx_len;
#if HAL_USE_SERIAL == TRUE
SerialConfig sercfg;
#endif
@ -157,7 +157,7 @@ private:
// we use in-task ring buffers to reduce the system call cost
// of ::read() and ::write() in the main loop
#ifndef HAL_UART_NODMA
bool tx_bounce_buf_ready;
volatile bool tx_bounce_buf_ready;
volatile uint8_t rx_bounce_idx;
uint8_t *rx_bounce_buf[2];
uint8_t *tx_bounce_buf;