dmaengine: tegra-apb: Don't save/restore IRQ flags in interrupt handler
PATCH 1/2 dmaengine: tegra-apb: Don't save/restore IRQ flags in interrupt handler
The interrupt is already disabled while interrupt handler is running, and thus, there is no need to save/restore the IRQ flags within the spinlock.
Signed-off-by Dmitry Osipenko <[email protected]>
--- drivers/dma/tegra20-apb-dma.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
drivers/dma/tegra20-apb-dma.c +3 -4
@@ -672,10 +672,9 @@ static void tegra_dma_tasklet(unsigned long data)static irqreturn_t tegra_dma_isr (int irq,void *dev_id) {struct tegra_dma_channel *tdc = dev_id; -unsigned long flags;u32 status; -spin_lock_irqsave (&tdc->lock, flags); +spin_lock (&tdc->lock);trace_tegra_dma_isr (&tdc->dma_chan, irq); status =tdc_read (tdc,TEGRA_APBDMA_CHAN_STATUS );
@@ -683,11 +682,11 @@ static irqreturn_t tegra_dma_isr(int irq, void *dev_id)tdc_write (tdc,TEGRA_APBDMA_CHAN_STATUS , status); tdc->isr_handler (tdc, false);tasklet_schedule (&tdc->tasklet); -spin_unlock_irqrestore (&tdc->lock, flags); +spin_unlock (&tdc->lock);return IRQ_HANDLED ; } -spin_unlock_irqrestore (&tdc->lock, flags); +spin_unlock (&tdc->lock);dev_info (tdc2dev (tdc),"Interrupt already served status 0x%08x\n" , status);
Signature · 1 line
2.25.1
PATCH 2/2 dmaengine: tegra-apb: Improve DMA synchronization
Boot CPU0 always handles DMA interrupts and under some rare circumstances it could stuck in uninterruptible state for a significant time (like in a case of KASAN + NFS root). In this case sibling CPU, which waits for DMA transfer completion, will get a DMA transfer timeout. In order to handle this rare condition, interrupt status needs to be polled until interrupt is handled.
Signed-off-by Dmitry Osipenko <[email protected]>
--- drivers/dma/tegra20-apb-dma.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
drivers/dma/tegra20-apb-dma.c +24 -0
@@ -24,6 +24,7 @@#include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/slab.h> +#include <linux/wait.h> #include "dmaengine.h"
@@ -202,6 +203,8 @@ struct tegra_dma_channel {unsigned int slave_id;struct dma_slave_config dma_sconfig;struct tegra_dma_channel_regs channel_reg; + +struct wait_queue_head wq; };/* tegra_dma: Tegra DMA specific information */
@@ -682,6 +685,7 @@ static irqreturn_t tegra_dma_isr(int irq, void *dev_id)tdc_write (tdc,TEGRA_APBDMA_CHAN_STATUS , status); tdc->isr_handler (tdc, false);tasklet_schedule (&tdc->tasklet); +wake_up_all (&tdc->wq);spin_unlock (&tdc->lock);return IRQ_HANDLED ; }
@@ -798,10 +802,29 @@ static int tegra_dma_terminate_all(struct dma_chan *dc)return 0 ; } +static bool tegra_dma_eoc_interrupt_deasserted (struct tegra_dma_channel *tdc) +{ +unsigned long flags; +u32 status; + +spin_lock_irqsave (&tdc->lock, flags); + status =tdc_read (tdc,TEGRA_APBDMA_CHAN_STATUS ); +spin_unlock_irqrestore (&tdc->lock, flags); + +return !(status &TEGRA_APBDMA_STATUS_ISE_EOC ); +} +static void tegra_dma_synchronize (struct dma_chan *dc) {struct tegra_dma_channel *tdc =to_tegra_dma_chan (dc); +/* +* CPU, which handles interrupt, could be busy in +* uninterruptible state, in this case sibling CPU +* should wait until interrupt is handled. +*/ +wait_event (tdc->wq,tegra_dma_eoc_interrupt_deasserted (tdc)); +tasklet_kill (&tdc->tasklet); }
@@ -1495,6 +1518,7 @@ static int tegra_dma_probe(struct platform_device *pdev)tasklet_init (&tdc->tasklet, tegra_dma_tasklet, (unsigned long )tdc);spin_lock_init (&tdc->lock); +init_waitqueue_head (&tdc->wq);INIT_LIST_HEAD (&tdc->pending_sg_req);INIT_LIST_HEAD (&tdc->free_sg_req);
Signature · 1 line
2.25.1