mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 06:02:05 +00:00
dmaengine: stm32-dma: fix potential race between pause and resume
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.
Fixes: 099a9a94be
("dmaengine: stm32-dma: add device_pause/device_resume support")
Signed-off-by: Amelie Delaunay <amelie.delaunay@foss.st.com>
Link: https://lore.kernel.org/r/20221024083611.132588-1-amelie.delaunay@foss.st.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
parent
ac2b9f34f0
commit
140fd5e74a
@ -675,6 +675,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->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);
|
dev_dbg(chan2dev(chan), "vchan %pK: paused\n", &chan->vchan);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -789,9 +791,7 @@ static irqreturn_t stm32_dma_chan_irq(int irq, void *devid)
|
|||||||
if (status & STM32_DMA_TCI) {
|
if (status & STM32_DMA_TCI) {
|
||||||
stm32_dma_irq_clear(chan, STM32_DMA_TCI);
|
stm32_dma_irq_clear(chan, STM32_DMA_TCI);
|
||||||
if (scr & STM32_DMA_SCR_TCIE) {
|
if (scr & STM32_DMA_SCR_TCIE) {
|
||||||
if (chan->status == DMA_PAUSED && !(scr & STM32_DMA_SCR_EN))
|
if (chan->status != DMA_PAUSED)
|
||||||
stm32_dma_handle_chan_paused(chan);
|
|
||||||
else
|
|
||||||
stm32_dma_handle_chan_done(chan, scr);
|
stm32_dma_handle_chan_done(chan, scr);
|
||||||
}
|
}
|
||||||
status &= ~STM32_DMA_TCI;
|
status &= ~STM32_DMA_TCI;
|
||||||
@ -838,13 +838,11 @@ static int stm32_dma_pause(struct dma_chan *c)
|
|||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
spin_lock_irqsave(&chan->vchan.lock, flags);
|
spin_lock_irqsave(&chan->vchan.lock, flags);
|
||||||
|
|
||||||
ret = stm32_dma_disable_chan(chan);
|
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)
|
if (!ret)
|
||||||
chan->status = DMA_PAUSED;
|
stm32_dma_handle_chan_paused(chan);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&chan->vchan.lock, flags);
|
spin_unlock_irqrestore(&chan->vchan.lock, flags);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
Loading…
Reference in New Issue
Block a user