From b79646f0a0cbfe48558980215794c178d814585e Mon Sep 17 00:00:00 2001 From: Scott Peterson Date: Thu, 11 Nov 2010 15:29:42 -0800 Subject: [ARM/Tegra] Solve DMA race condition on close Fixed a race condition when calling NvRmDmaAbort while a DMA transfer is in progress causing a kernel panic Bug 750317 Change-Id: I280971e509857d08e7bf28d676513814095c30ca Reviewed-on: http://git-master/r/10633 Reviewed-by: Scott Peterson Tested-by: Scott Peterson Reviewed-by: Yu-Huan Hsu Reviewed-by: Varun Colbert Tested-by: Varun Colbert --- arch/arm/mach-tegra/dma.c | 12 ++++++++---- arch/arm/mach-tegra/nvrm/io/common/nvrm_dma.c | 14 ++++++++------ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c index a741cb9b3b2a..1cfe13912116 100644 --- a/arch/arm/mach-tegra/dma.c +++ b/arch/arm/mach-tegra/dma.c @@ -143,10 +143,6 @@ static void tegra_dma_update_hw_partial(struct tegra_dma_channel *ch, struct tegra_dma_req *req); static void tegra_dma_init_hw(struct tegra_dma_channel *ch); -void tegra_dma_flush(struct tegra_dma_channel *ch) -{ -} -EXPORT_SYMBOL(tegra_dma_flush); void tegra_dma_dequeue(struct tegra_dma_channel *ch) { @@ -184,6 +180,7 @@ void tegra_dma_stop(struct tegra_dma_channel *ch) writel(status, ch->addr + APB_DMA_CHAN_STA); } + int tegra_dma_cancel(struct tegra_dma_channel *ch) { unsigned long irq_flags; @@ -201,6 +198,13 @@ int tegra_dma_cancel(struct tegra_dma_channel *ch) return 0; } +void tegra_dma_flush(struct tegra_dma_channel *ch) +{ + tegra_dma_cancel(ch); +} +EXPORT_SYMBOL(tegra_dma_flush); + + /* should be called with the channel lock held */ static unsigned int dma_active_count(struct tegra_dma_channel *ch, struct tegra_dma_req *req, unsigned int status) diff --git a/arch/arm/mach-tegra/nvrm/io/common/nvrm_dma.c b/arch/arm/mach-tegra/nvrm/io/common/nvrm_dma.c index 0734dc9dda18..b0a304c73db8 100644 --- a/arch/arm/mach-tegra/nvrm/io/common/nvrm_dma.c +++ b/arch/arm/mach-tegra/nvrm/io/common/nvrm_dma.c @@ -61,13 +61,13 @@ static void dma_complete_work(struct work_struct *work) spin_lock(&action->dma->lock); list_del(&action->node); - spin_unlock(&action->dma->lock); if (action->dma_sem) { if (action->req.status==TEGRA_DMA_REQ_SUCCESS) NvOsSemaphoreSignal(action->dma_sem); NvOsSemaphoreDestroy(action->dma_sem); } + spin_unlock(&action->dma->lock); kfree(action); } @@ -328,30 +328,32 @@ NvError NvRmDmaStartDmaTransfer(NvRmDmaHandle dma, NvRmDmaClientBuffer *b, return e; } + void NvRmDmaAbort(NvRmDmaHandle dma) { if (!dma) return; + tegra_dma_flush(dma->ch); + spin_lock(&dma->lock); while (!list_empty(&dma->req_list)) { struct dma_action *action; - struct tegra_dma_req *req; action = list_first_entry(&dma->req_list, struct dma_action, node); - req = &action->req; - spin_unlock(&dma->lock); - tegra_dma_dequeue_req(dma->ch, req); + if (action->dma_sem) NvOsSemaphoreDestroy(action->dma_sem); + list_del(&action->node); kfree(action); - spin_lock(&dma->lock); } spin_unlock(&dma->lock); } + + NvError NvRmDmaGetCapabilities(NvRmDeviceHandle rm, NvRmDmaCapabilities *caps) { if (!caps) -- cgit v1.2.3