Skip to content

Commit

Permalink
CHROMIUM: dmaengine: tegra-adma: fix race between tasklet/terminate_all
Browse files Browse the repository at this point in the history
tegra_adma_terminate_all will empty the callback list that gets run
in the bottom half of the interrupt handler, tegra_adma_tasklet.
But if a callback is in progress, it will still run.

For example, this can cause a race when pcm_dmaengine closes a
stream and frees resources used by the DMA callback.  We need to
make sure all tasklets are done before returning from
tegra_adma_terminate_all to safely close the stream.

BUG=chrome-os-partner:46247
TEST=Audio playback

Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
[cfreeman@nvidia.com: applied manually and added commit message]
Signed-off-by: Christopher Freeman <cfreeman@nvidia.com>
Change-Id: I5d75990e8efbc008792576bea260a5a4cbe8f2cb
Reviewed-on: https://chromium-review.googlesource.com/304920
Commit-Ready: Andrew Bresticker <abrestic@chromium.org>
Reviewed-by: Andrew Bresticker <abrestic@chromium.org>
  • Loading branch information
Jon Hunter authored and chrome-bot committed Oct 9, 2015
1 parent 1ca2ab9 commit 92cd07d
Showing 1 changed file with 16 additions and 0 deletions.
16 changes: 16 additions & 0 deletions drivers/dma/tegra210-adma.c
Original file line number Diff line number Diff line change
@@ -125,6 +125,7 @@ struct tegra_adma_chan {
/* ISR handler and tasklet for bottom half of isr handling */
dma_isr_handler isr_handler;
struct tasklet_struct tasklet;
bool tasklet_active;
dma_async_tx_callback callback;
void *callback_param;

@@ -594,6 +595,7 @@ static void tegra_adma_tasklet(unsigned long data)
int cb_count;

spin_lock_irqsave(&tdc->lock, flags);
tdc->tasklet_active = true;
while (!list_empty(&tdc->cb_desc)) {
dma_desc = list_first_entry(&tdc->cb_desc,
typeof(*dma_desc), cb_node);
@@ -607,6 +609,7 @@ static void tegra_adma_tasklet(unsigned long data)
callback(callback_param);
spin_lock_irqsave(&tdc->lock, flags);
}
tdc->tasklet_active = false;
spin_unlock_irqrestore(&tdc->lock, flags);
}

@@ -719,7 +722,20 @@ static void tegra_adma_terminate_all(struct dma_chan *dc)
sgreq->dma_desc->bytes_transferred +=
get_current_xferred_count(tdc, sgreq, tc);
}

tegra_adma_resume(tdc);
/*
* Check for any running tasklets and kill them
*/
if (tdc->tasklet_active) {
tdc->busy = true;
spin_unlock_irqrestore(&tdc->lock, flags);
dev_warn(tdc2dev(tdc), "Killing tasklet\n");
tasklet_kill(&tdc->tasklet);
spin_lock_irqsave(&tdc->lock, flags);
tdc->busy = false;
}

skip_dma_stop:
tegra_adma_abort_all(tdc);

0 comments on commit 92cd07d

Please sign in to comment.