From b13c0c62ddf7a3a7d5b96fed8ea80f21f3bb2dad Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Wed, 17 Jul 2013 20:00:13 +0300 Subject: [PATCH 12/13] mmc: omap_hsmmc: Fix the crashes due to the interrupts racing --- drivers/mmc/host/omap_hsmmc.c | 120 +++++++++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 32 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 1f9ff97..91e2954 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -171,7 +171,7 @@ struct omap_hsmmc_host { unsigned char power_mode; int suspended; int irq; - int use_dma, dma_ch; + int use_dma; struct dma_chan *tx_chan; struct dma_chan *rx_chan; int slot_id; @@ -180,10 +180,15 @@ struct omap_hsmmc_host { int protect_card; int reqs_blocked; int use_reg; - int req_in_progress; struct omap_hsmmc_next next_data; struct omap_mmc_platform_data *pdata; + + unsigned int req_flags; +#define RQF_REQ_IN_PROGRESS (1 << 0) +#define RQF_DMA_IN_PROGRESS (1 << 1) +#define RQF_REQ_DONE (1 << 2) +#define RQF_DMA_DONE (1 << 3) }; static int omap_hsmmc_card_detect(struct device *dev, int slot) @@ -803,7 +808,8 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd, if (host->use_dma) cmdreg |= DMAE; - host->req_in_progress = 1; + host->req_flags |= RQF_REQ_IN_PROGRESS; + host->req_flags &= ~RQF_REQ_DONE; OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg); OMAP_HSMMC_WRITE(host->base, CMD, cmdreg); @@ -826,19 +832,34 @@ static struct dma_chan *omap_hsmmc_get_dma_chan(struct omap_hsmmc_host *host, static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq) { - int dma_ch; + int completed; unsigned long flags; spin_lock_irqsave(&host->irq_lock, flags); - host->req_in_progress = 0; - dma_ch = host->dma_ch; - spin_unlock_irqrestore(&host->irq_lock, flags); + + host->req_flags &= ~RQF_REQ_IN_PROGRESS; + host->req_flags |= RQF_REQ_DONE; + + /* completed? */ + if (mrq->data && host->use_dma) + completed = (host->req_flags & RQF_DMA_DONE) == RQF_DMA_DONE; + else + completed = 1; omap_hsmmc_disable_irq(host); + /* Do not complete the request if DMA is still in progress */ - if (mrq->data && host->use_dma && dma_ch != -1) + if (!completed) { + spin_unlock_irqrestore(&host->irq_lock, flags); + pr_debug("%s: not completed!\n", __func__); return; + } + + /* clear the flags now */ + host->req_flags &= ~(RQF_REQ_DONE | RQF_DMA_DONE); host->mrq = NULL; + spin_unlock_irqrestore(&host->irq_lock, flags); + mmc_request_done(host->mmc, mrq); } @@ -855,6 +876,7 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data) if (host->cmd && host->cmd->opcode == 6 && host->response_busy) { host->response_busy = 0; + pr_debug("%s: response_busy = 0\n", __func__); return; } @@ -870,9 +892,11 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data) data->bytes_xfered = 0; if (!data->stop) { + pr_debug("%s: calling omap_hsmmc_request_done\n", __func__); omap_hsmmc_request_done(host, data->mrq); return; } + pr_debug("%s: calling omap_hsmmc_start_command\n", __func__); omap_hsmmc_start_command(host, data->stop, NULL); } @@ -882,6 +906,8 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data) static void omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd) { + unsigned long flags; + host->cmd = NULL; if (cmd->flags & MMC_RSP_PRESENT) { @@ -898,6 +924,18 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd) } if ((host->data == NULL && !host->response_busy) || cmd->error) omap_hsmmc_request_done(host, cmd->mrq); + else { + spin_lock_irqsave(&host->irq_lock, flags); + /* we use DMA, and DMA is completed - kick the can */ + if ((host->req_flags & RQF_DMA_DONE) != 0) { + host->req_flags &= ~(RQF_REQ_IN_PROGRESS | RQF_REQ_DONE | RQF_DMA_DONE); + host->mrq = NULL; + mmc_request_done(host->mmc, cmd->mrq); + } else { + pr_debug("%s: not calling omap_hsmmc_request_done!\n", __func__); + } + spin_unlock_irqrestore(&host->irq_lock, flags); + } } /* @@ -905,17 +943,19 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd) */ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno) { - int dma_ch; + int dma_in_progress; unsigned long flags; host->data->error = errno; spin_lock_irqsave(&host->irq_lock, flags); - dma_ch = host->dma_ch; - host->dma_ch = -1; + dma_in_progress = host->use_dma && + (host->req_flags & RQF_DMA_IN_PROGRESS) != 0; + host->req_flags &= ~RQF_DMA_IN_PROGRESS; + host->req_flags |= RQF_DMA_DONE; spin_unlock_irqrestore(&host->irq_lock, flags); - if (host->use_dma && dma_ch != -1) { + if (dma_in_progress) { struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, host->data); dmaengine_terminate_all(chan); @@ -1005,16 +1045,22 @@ static void hsmmc_command_incomplete(struct omap_hsmmc_host *host, int err, int end_cmd) { if (end_cmd) { + pr_debug("%s end_cmd\n", __func__); omap_hsmmc_reset_controller_fsm(host, SRC); if (host->cmd) host->cmd->error = err; } if (host->data) { + pr_debug("%s host->data; resetting dma\n", __func__); omap_hsmmc_reset_controller_fsm(host, SRD); omap_hsmmc_dma_cleanup(host, err); - } else if (host->mrq && host->mrq->cmd) + } else if (host->mrq && host->mrq->cmd) { + pr_debug("%s error\n", __func__); host->mrq->cmd->error = err; + } else { + pr_debug("%s nothing\n", __func__); + } } static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status) @@ -1055,13 +1101,13 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id) struct omap_hsmmc_host *host = dev_id; int status; - status = OMAP_HSMMC_READ(host->base, STAT); - while (status & INT_EN_MASK && host->req_in_progress) { - omap_hsmmc_do_irq(host, status); + while ((status = OMAP_HSMMC_READ(host->base, STAT)) & INT_EN_MASK) { + + if (host->req_flags & RQF_REQ_IN_PROGRESS) + omap_hsmmc_do_irq(host, status); /* Flush posted write */ OMAP_HSMMC_WRITE(host->base, STAT, status); - status = OMAP_HSMMC_READ(host->base, STAT); } return IRQ_HANDLED; @@ -1199,13 +1245,15 @@ static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id) static void omap_hsmmc_dma_callback(void *param) { struct omap_hsmmc_host *host = param; + struct mmc_request *mrq = host->mrq; struct dma_chan *chan; struct mmc_data *data; - int req_in_progress; + int completed; spin_lock_irq(&host->irq_lock); - if (host->dma_ch < 0) { + if ((host->req_flags & RQF_DMA_IN_PROGRESS) == 0) { spin_unlock_irq(&host->irq_lock); + pr_debug("%s: No DMA in progress!\n", __func__); return; } @@ -1216,17 +1264,22 @@ static void omap_hsmmc_dma_callback(void *param) data->sg, data->sg_len, omap_hsmmc_get_dma_dir(host, data)); - req_in_progress = host->req_in_progress; - host->dma_ch = -1; - spin_unlock_irq(&host->irq_lock); + host->req_flags &= ~RQF_DMA_IN_PROGRESS; + host->req_flags |= RQF_DMA_DONE; - /* If DMA has finished after TC, complete the request */ - if (!req_in_progress) { - struct mmc_request *mrq = host->mrq; + completed = (host->req_flags & RQF_REQ_DONE) != 0; - host->mrq = NULL; - mmc_request_done(host->mmc, mrq); + if (!completed) { + spin_unlock_irq(&host->irq_lock); + pr_debug("%s: not completed\n", __func__); + return; } + + host->req_flags &= ~(RQF_REQ_DONE | RQF_DMA_DONE); + host->mrq = NULL; + spin_unlock_irq(&host->irq_lock); + + mmc_request_done(host->mmc, mrq); } static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host, @@ -1294,7 +1347,7 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host, */ return -EINVAL; - BUG_ON(host->dma_ch != -1); + BUG_ON((host->req_flags & RQF_DMA_IN_PROGRESS) != 0); chan = omap_hsmmc_get_dma_chan(host, data); @@ -1328,7 +1381,7 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host, /* Does not fail */ dmaengine_submit(tx); - host->dma_ch = 1; + host->req_flags |= RQF_DMA_IN_PROGRESS; dma_async_issue_pending(chan); @@ -1448,8 +1501,11 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req) struct omap_hsmmc_host *host = mmc_priv(mmc); int err; - BUG_ON(host->req_in_progress); - BUG_ON(host->dma_ch != -1); + BUG_ON((host->req_flags & RQF_REQ_IN_PROGRESS) != 0); + BUG_ON((host->req_flags & RQF_REQ_DONE) != 0); + BUG_ON((host->req_flags & RQF_DMA_IN_PROGRESS) != 0); + BUG_ON((host->req_flags & RQF_DMA_DONE) != 0); + if (host->protect_card) { if (host->reqs_blocked < 3) { /* @@ -1826,13 +1882,13 @@ static int omap_hsmmc_probe(struct platform_device *pdev) host->pdata = pdata; host->dev = &pdev->dev; host->use_dma = 1; - host->dma_ch = -1; host->irq = irq; host->slot_id = 0; host->mapbase = res->start + pdata->reg_offset; host->base = ioremap(host->mapbase, SZ_4K); host->power_mode = MMC_POWER_OFF; host->next_data.cookie = 1; + host->req_flags = 0; platform_set_drvdata(pdev, host); -- 1.8.2.1