summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Peterson <speterson@nvidia.com>2010-11-11 15:29:42 -0800
committerVarun Colbert <vcolbert@nvidia.com>2010-11-15 20:14:57 -0800
commitb79646f0a0cbfe48558980215794c178d814585e (patch)
treead1a23c67a023e785c161e1c67be3050286a72a9
parent696d14b5dee46679980c9a41883d29feb19fa9b4 (diff)
[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 <speterson@nvidia.com> Tested-by: Scott Peterson <speterson@nvidia.com> Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com> Reviewed-by: Varun Colbert <vcolbert@nvidia.com> Tested-by: Varun Colbert <vcolbert@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/dma.c12
-rw-r--r--arch/arm/mach-tegra/nvrm/io/common/nvrm_dma.c14
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)