Skip to content

Commit cbf6bb5

Browse files
Phil Elwellpopcornmix
Phil Elwell
authored andcommitted
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 <[email protected]>
1 parent 563aa8c commit cbf6bb5

File tree

1 file changed

+11
-1
lines changed

1 file changed

+11
-1
lines changed

drivers/mmc/host/bcm2835-mmc.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ struct bcm2835_host {
115115

116116
bool have_dma;
117117
bool use_dma;
118+
bool wait_for_dma;
118119
/*end of DMA part*/
119120

120121
int max_delay; /* maximum length of time spent waiting */
@@ -341,6 +342,8 @@ static void bcm2835_mmc_dma_complete(void *param)
341342

342343
spin_lock_irqsave(&host->lock, flags);
343344

345+
host->use_dma = false;
346+
344347
if (host->data && !(host->data->flags & MMC_DATA_WRITE)) {
345348
/* otherwise handled in SDHCI IRQ */
346349
dma_chan = host->dma_chan_rxtx;
@@ -351,6 +354,9 @@ static void bcm2835_mmc_dma_complete(void *param)
351354
dir_data);
352355

353356
bcm2835_mmc_finish_data(host);
357+
} else if (host->wait_for_dma) {
358+
host->wait_for_dma = false;
359+
tasklet_schedule(&host->finish_tasklet);
354360
}
355361

356362
spin_unlock_irqrestore(&host->lock, flags);
@@ -690,6 +696,7 @@ void bcm2835_mmc_send_command(struct bcm2835_host *host, struct mmc_command *cmd
690696
mod_timer(&host->timer, timeout);
691697

692698
host->cmd = cmd;
699+
host->use_dma = false;
693700

694701
bcm2835_mmc_prepare_data(host, cmd);
695702

@@ -759,8 +766,11 @@ static void bcm2835_mmc_finish_data(struct bcm2835_host *host)
759766
}
760767

761768
bcm2835_mmc_send_command(host, data->stop);
762-
} else
769+
} else if (host->use_dma) {
770+
host->wait_for_dma = true;
771+
} else {
763772
tasklet_schedule(&host->finish_tasklet);
773+
}
764774
}
765775

766776
static void bcm2835_mmc_finish_command(struct bcm2835_host *host)

0 commit comments

Comments
 (0)