Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mmc: dw_mmc: Add data CRC error injection

This driver has had problems when handling data errors. Add fault
injection support so that the abort handling can be easily triggered and
regression-tested. A hrtimer is used to indicate a data CRC error at
various points during the data transfer.

Note that for the recent problem with hangs in the case of some data CRC
errors, a udelay(10) inserted at the start of send_stop_abort() greatly
helped in triggering the error, but I've not included this as part of
the fault injection support since it seemed too specific.

Signed-off-by: Vincent Whitchurch <vincent.whitchurch@axis.com>
Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com>
Link: https://lore.kernel.org/r/20210701080534.23138-1-vincent.whitchurch@axis.com
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

authored by

Vincent Whitchurch and committed by
Ulf Hansson
2b8ac062 69606847

+80
+73
drivers/mmc/host/dw_mmc.c
··· 17 17 #include <linux/interrupt.h> 18 18 #include <linux/iopoll.h> 19 19 #include <linux/ioport.h> 20 + #include <linux/ktime.h> 20 21 #include <linux/module.h> 21 22 #include <linux/platform_device.h> 22 23 #include <linux/pm_runtime.h> 24 + #include <linux/prandom.h> 23 25 #include <linux/seq_file.h> 24 26 #include <linux/slab.h> 25 27 #include <linux/stat.h> ··· 183 181 &host->pending_events); 184 182 debugfs_create_xul("completed_events", S_IRUSR, root, 185 183 &host->completed_events); 184 + #ifdef CONFIG_FAULT_INJECTION 185 + fault_create_debugfs_attr("fail_data_crc", root, &host->fail_data_crc); 186 + #endif 186 187 } 187 188 #endif /* defined(CONFIG_DEBUG_FS) */ 188 189 ··· 1793 1788 .prepare_hs400_tuning = dw_mci_prepare_hs400_tuning, 1794 1789 }; 1795 1790 1791 + #ifdef CONFIG_FAULT_INJECTION 1792 + static enum hrtimer_restart dw_mci_fault_timer(struct hrtimer *t) 1793 + { 1794 + struct dw_mci *host = container_of(t, struct dw_mci, fault_timer); 1795 + unsigned long flags; 1796 + 1797 + spin_lock_irqsave(&host->irq_lock, flags); 1798 + 1799 + if (!host->data_status) 1800 + host->data_status = SDMMC_INT_DCRC; 1801 + set_bit(EVENT_DATA_ERROR, &host->pending_events); 1802 + tasklet_schedule(&host->tasklet); 1803 + 1804 + spin_unlock_irqrestore(&host->irq_lock, flags); 1805 + 1806 + return HRTIMER_NORESTART; 1807 + } 1808 + 1809 + static void dw_mci_start_fault_timer(struct dw_mci *host) 1810 + { 1811 + struct mmc_data *data = host->data; 1812 + 1813 + if (!data || data->blocks <= 1) 1814 + return; 1815 + 1816 + if (!should_fail(&host->fail_data_crc, 1)) 1817 + return; 1818 + 1819 + /* 1820 + * Try to inject the error at random points during the data transfer. 1821 + */ 1822 + hrtimer_start(&host->fault_timer, 1823 + ms_to_ktime(prandom_u32() % 25), 1824 + HRTIMER_MODE_REL); 1825 + } 1826 + 1827 + static void dw_mci_stop_fault_timer(struct dw_mci *host) 1828 + { 1829 + hrtimer_cancel(&host->fault_timer); 1830 + } 1831 + 1832 + static void dw_mci_init_fault(struct dw_mci *host) 1833 + { 1834 + host->fail_data_crc = (struct fault_attr) FAULT_ATTR_INITIALIZER; 1835 + 1836 + hrtimer_init(&host->fault_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 1837 + host->fault_timer.function = dw_mci_fault_timer; 1838 + } 1839 + #else 1840 + static void dw_mci_init_fault(struct dw_mci *host) 1841 + { 1842 + } 1843 + 1844 + static void dw_mci_start_fault_timer(struct dw_mci *host) 1845 + { 1846 + } 1847 + 1848 + static void dw_mci_stop_fault_timer(struct dw_mci *host) 1849 + { 1850 + } 1851 + #endif 1852 + 1796 1853 static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) 1797 1854 __releases(&host->lock) 1798 1855 __acquires(&host->lock) ··· 2169 2102 break; 2170 2103 } 2171 2104 2105 + dw_mci_stop_fault_timer(host); 2172 2106 host->data = NULL; 2173 2107 set_bit(EVENT_DATA_COMPLETE, &host->completed_events); 2174 2108 err = dw_mci_data_complete(host, data); ··· 2219 2151 if (mrq->cmd->error && mrq->data) 2220 2152 dw_mci_reset(host); 2221 2153 2154 + dw_mci_stop_fault_timer(host); 2222 2155 host->cmd = NULL; 2223 2156 host->data = NULL; 2224 2157 ··· 2669 2600 2670 2601 set_bit(EVENT_CMD_COMPLETE, &host->pending_events); 2671 2602 tasklet_schedule(&host->tasklet); 2603 + 2604 + dw_mci_start_fault_timer(host); 2672 2605 } 2673 2606 2674 2607 static void dw_mci_handle_cd(struct dw_mci *host) ··· 3293 3222 spin_lock_init(&host->lock); 3294 3223 spin_lock_init(&host->irq_lock); 3295 3224 INIT_LIST_HEAD(&host->queue); 3225 + 3226 + dw_mci_init_fault(host); 3296 3227 3297 3228 /* 3298 3229 * Get the host data width - this assumes that HCON has been set with
+7
drivers/mmc/host/dw_mmc.h
··· 14 14 #include <linux/mmc/core.h> 15 15 #include <linux/dmaengine.h> 16 16 #include <linux/reset.h> 17 + #include <linux/fault-inject.h> 18 + #include <linux/hrtimer.h> 17 19 #include <linux/interrupt.h> 18 20 19 21 enum dw_mci_state { ··· 232 230 struct timer_list cmd11_timer; 233 231 struct timer_list cto_timer; 234 232 struct timer_list dto_timer; 233 + 234 + #ifdef CONFIG_FAULT_INJECTION 235 + struct fault_attr fail_data_crc; 236 + struct hrtimer fault_timer; 237 + #endif 235 238 }; 236 239 237 240 /* DMA ops for Internal/External DMAC interface */