|
@@ -24,6 +24,8 @@
|
|
|
#include <linux/regulator/consumer.h>
|
|
|
#include <linux/pm_runtime.h>
|
|
|
#include <linux/suspend.h>
|
|
|
+#include <linux/fault-inject.h>
|
|
|
+#include <linux/random.h>
|
|
|
|
|
|
#include <linux/mmc/card.h>
|
|
|
#include <linux/mmc/host.h>
|
|
@@ -83,6 +85,43 @@ static void mmc_flush_scheduled_work(void)
|
|
|
flush_workqueue(workqueue);
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_FAIL_MMC_REQUEST
|
|
|
+
|
|
|
+/*
|
|
|
+ * Internal function. Inject random data errors.
|
|
|
+ * If mmc_data is NULL no errors are injected.
|
|
|
+ */
|
|
|
+static void mmc_should_fail_request(struct mmc_host *host,
|
|
|
+ struct mmc_request *mrq)
|
|
|
+{
|
|
|
+ struct mmc_command *cmd = mrq->cmd;
|
|
|
+ struct mmc_data *data = mrq->data;
|
|
|
+ static const int data_errors[] = {
|
|
|
+ -ETIMEDOUT,
|
|
|
+ -EILSEQ,
|
|
|
+ -EIO,
|
|
|
+ };
|
|
|
+
|
|
|
+ if (!data)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (cmd->error || data->error ||
|
|
|
+ !should_fail(&host->fail_mmc_request, data->blksz * data->blocks))
|
|
|
+ return;
|
|
|
+
|
|
|
+ data->error = data_errors[random32() % ARRAY_SIZE(data_errors)];
|
|
|
+ data->bytes_xfered = (random32() % (data->bytes_xfered >> 9)) << 9;
|
|
|
+}
|
|
|
+
|
|
|
+#else /* CONFIG_FAIL_MMC_REQUEST */
|
|
|
+
|
|
|
+static inline void mmc_should_fail_request(struct mmc_host *host,
|
|
|
+ struct mmc_request *mrq)
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+#endif /* CONFIG_FAIL_MMC_REQUEST */
|
|
|
+
|
|
|
/**
|
|
|
* mmc_request_done - finish processing an MMC request
|
|
|
* @host: MMC host which completed request
|
|
@@ -109,6 +148,8 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
|
|
|
cmd->error = 0;
|
|
|
host->ops->request(host, mrq);
|
|
|
} else {
|
|
|
+ mmc_should_fail_request(host, mrq);
|
|
|
+
|
|
|
led_trigger_event(host->led, LED_OFF);
|
|
|
|
|
|
pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",
|