mmc: dw_mmc: Only inject fault before done/error
authorVincent Whitchurch <vincent.whitchurch@axis.com>
Wed, 25 Aug 2021 11:42:13 +0000 (13:42 +0200)
committerUlf Hansson <ulf.hansson@linaro.org>
Mon, 6 Sep 2021 16:02:16 +0000 (18:02 +0200)
The fault injection function can set EVENT_DATA_ERROR but skip the
setting of ->data_status to an error status if it hits just after a data
over interrupt.  This confuses the tasklet which can later end up
triggering the WARN_ON(host->cmd || ..) in dw_mci_request_end() since
dw_mci_data_complete() would return success.

Prevent the fault injection function from doing this since this is not a
real case, and ensure that the fault injection doesn't race with a real
error either.

Signed-off-by: Vincent Whitchurch <vincent.whitchurch@axis.com>
Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com>
Fixes: 2b8ac062f337 ("mmc: dw_mmc: Add data CRC error injection")
Link: https://lore.kernel.org/r/20210825114213.7429-1-vincent.whitchurch@axis.com
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/dw_mmc.c

index 6578cc6..380f9aa 100644 (file)
@@ -1802,10 +1802,15 @@ static enum hrtimer_restart dw_mci_fault_timer(struct hrtimer *t)
 
        spin_lock_irqsave(&host->irq_lock, flags);
 
-       if (!host->data_status)
+       /*
+        * Only inject an error if we haven't already got an error or data over
+        * interrupt.
+        */
+       if (!host->data_status) {
                host->data_status = SDMMC_INT_DCRC;
-       set_bit(EVENT_DATA_ERROR, &host->pending_events);
-       tasklet_schedule(&host->tasklet);
+               set_bit(EVENT_DATA_ERROR, &host->pending_events);
+               tasklet_schedule(&host->tasklet);
+       }
 
        spin_unlock_irqrestore(&host->irq_lock, flags);
 
@@ -2721,12 +2726,16 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
                }
 
                if (pending & DW_MCI_DATA_ERROR_FLAGS) {
+                       spin_lock(&host->irq_lock);
+
                        /* if there is an error report DATA_ERROR */
                        mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
                        host->data_status = pending;
                        smp_wmb(); /* drain writebuffer */
                        set_bit(EVENT_DATA_ERROR, &host->pending_events);
                        tasklet_schedule(&host->tasklet);
+
+                       spin_unlock(&host->irq_lock);
                }
 
                if (pending & SDMMC_INT_DATA_OVER) {