From: Phil Elwell Date: Tue, 18 Jul 2017 14:30:48 +0000 (+0100) Subject: bcm2835-mmc: Prevent DMA race condition X-Git-Tag: archive/raspbian/4.9.82-1+deb9u3+rpi1_jessie~5^2~173 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=56bff375edd5d3f8276282f4c36f3d7460751cd7;p=linux-4.9.git bcm2835-mmc: Prevent DMA race condition The end of a read operation is triggered by the completion of the DMA transfer, but writes are complete when the data IRQ is raised. The bcm2835-mmc driver contains a race between the handling of the DMA completion interrupt and the submission of the next request. Fix the race by deferring the completion of the request until the DMA transfer finishes. Signed-off-by: Phil Elwell --- diff --git a/drivers/mmc/host/bcm2835-mmc.c b/drivers/mmc/host/bcm2835-mmc.c index 981db05de1ff..c4a5e992c6fb 100644 --- a/drivers/mmc/host/bcm2835-mmc.c +++ b/drivers/mmc/host/bcm2835-mmc.c @@ -115,6 +115,7 @@ struct bcm2835_host { bool have_dma; bool use_dma; + bool wait_for_dma; /*end of DMA part*/ int max_delay; /* maximum length of time spent waiting */ @@ -341,6 +342,8 @@ static void bcm2835_mmc_dma_complete(void *param) spin_lock_irqsave(&host->lock, flags); + host->use_dma = false; + if (host->data && !(host->data->flags & MMC_DATA_WRITE)) { /* otherwise handled in SDHCI IRQ */ dma_chan = host->dma_chan_rxtx; @@ -351,6 +354,9 @@ static void bcm2835_mmc_dma_complete(void *param) dir_data); bcm2835_mmc_finish_data(host); + } else if (host->wait_for_dma) { + host->wait_for_dma = false; + tasklet_schedule(&host->finish_tasklet); } spin_unlock_irqrestore(&host->lock, flags); @@ -690,6 +696,7 @@ void bcm2835_mmc_send_command(struct bcm2835_host *host, struct mmc_command *cmd mod_timer(&host->timer, timeout); host->cmd = cmd; + host->use_dma = false; bcm2835_mmc_prepare_data(host, cmd); @@ -759,8 +766,11 @@ static void bcm2835_mmc_finish_data(struct bcm2835_host *host) } bcm2835_mmc_send_command(host, data->stop); - } else + } else if (host->use_dma) { + host->wait_for_dma = true; + } else { tasklet_schedule(&host->finish_tasklet); + } } static void bcm2835_mmc_finish_command(struct bcm2835_host *host)