Skip to content

Commit

Permalink
dmaengine: stm32-dma: fix potential race between pause and resume
Browse files Browse the repository at this point in the history
When disabling dma channel, a TCF flag is set and as TCIE is enabled, an
interrupt is raised.
On a busy system, the interrupt may have latency and the user can ask for
dmaengine_resume while stm32-dma driver has not yet managed the complete
pause (backup of registers to restore state in resume).
To avoid such a case, instead of waiting the interrupt to backup the
registers, do it just after disabling the channel and discard Transfer
Complete interrupt in case the channel is paused.

Change-Id: I8d07cd3a2cbb9ff3056b104fea6cab10d90c32fd
Signed-off-by: Amelie Delaunay <[email protected]>
Reviewed-on: https://gerrit.st.com/c/mpu/oe/st/linux-stm32/+/252593
Reviewed-by: CITOOLS <[email protected]>
Reviewed-by: Fabien DESSENNE <[email protected]>
  • Loading branch information
ADESTM authored and fourmone committed Jul 5, 2022
1 parent 85c7123 commit 7d34eca
Showing 1 changed file with 6 additions and 8 deletions.
14 changes: 6 additions & 8 deletions drivers/dma/stm32-dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,8 @@ static void stm32_dma_handle_chan_paused(struct stm32_dma_chan *chan)

chan->chan_reg.dma_sndtr = stm32_dma_read(dmadev, STM32_DMA_SNDTR(chan->id));

chan->status = DMA_PAUSED;

dev_dbg(chan2dev(chan), "vchan %pK: paused\n", &chan->vchan);
}

Expand Down Expand Up @@ -1182,9 +1184,7 @@ static irqreturn_t stm32_dma_chan_irq(int irq, void *devid)
if (status & STM32_DMA_TCI) {
stm32_dma_irq_clear(chan, STM32_DMA_TCI);
if (scr & STM32_DMA_SCR_TCIE) {
if (chan->status == DMA_PAUSED && !(scr & STM32_DMA_SCR_EN))
stm32_dma_handle_chan_paused(chan);
else
if (chan->status != DMA_PAUSED)
stm32_dma_handle_chan_done(chan, scr);
}
status &= ~STM32_DMA_TCI;
Expand Down Expand Up @@ -1237,13 +1237,11 @@ static int stm32_dma_pause(struct dma_chan *c)
return -EPERM;

spin_lock_irqsave(&chan->vchan.lock, flags);

ret = stm32_dma_disable_chan(chan);
/*
* A transfer complete flag is set to indicate the end of transfer due to the stream
* interruption, so wait for interrupt
*/
if (!ret)
chan->status = DMA_PAUSED;
stm32_dma_handle_chan_paused(chan);

spin_unlock_irqrestore(&chan->vchan.lock, flags);

return ret;
Expand Down

0 comments on commit 7d34eca

Please sign in to comment.