scsi: ufs: Add fault injection support
authorBart Van Assche <bvanassche@acm.org>
Thu, 22 Jul 2021 03:34:39 +0000 (20:34 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 3 Aug 2021 11:27:42 +0000 (07:27 -0400)
Make it easier to test the UFS error handler and abort handler.

Link: https://lore.kernel.org/r/20210722033439.26550-19-bvanassche@acm.org
Acked-by: Bean Huo <beanhuo@micron.com>
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/ufs/Kconfig
drivers/scsi/ufs/Makefile
drivers/scsi/ufs/ufs-fault-injection.c [new file with mode: 0644]
drivers/scsi/ufs/ufs-fault-injection.h [new file with mode: 0644]
drivers/scsi/ufs/ufshcd.c

index ee650526c560590ce9a6d8caef3895f9a98b3c69..432df76e6318a6f909d5071609f86a72e264c663 100644 (file)
@@ -192,3 +192,10 @@ config SCSI_UFS_HPB
          L2P (logical to physical) map of UFS to host DRAM. The driver uses HPB
          read command by piggybacking physical page number for bypassing FTL (flash
          translation layer)'s L2P address translation.
+
+config SCSI_UFS_FAULT_INJECTION
+       bool "UFS Fault Injection Support"
+       depends on SCSI_UFSHCD && FAULT_INJECTION
+       help
+         Enable fault injection support in the UFS driver. This makes it easier
+         to test the UFS error handler and abort handler.
index cce9b3916f5b37063db8073c870e7da6f1f9d592..c407da9b517132f22c67f617ebc9993e3a47f6d0 100644 (file)
@@ -9,6 +9,7 @@ ufshcd-core-$(CONFIG_DEBUG_FS)          += ufs-debugfs.o
 ufshcd-core-$(CONFIG_SCSI_UFS_BSG)     += ufs_bsg.o
 ufshcd-core-$(CONFIG_SCSI_UFS_CRYPTO)  += ufshcd-crypto.o
 ufshcd-core-$(CONFIG_SCSI_UFS_HPB)     += ufshpb.o
+ufshcd-core-$(CONFIG_SCSI_UFS_FAULT_INJECTION) += ufs-fault-injection.o
 
 obj-$(CONFIG_SCSI_UFS_DWC_TC_PCI) += tc-dwc-g210-pci.o ufshcd-dwc.o tc-dwc-g210.o
 obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o ufshcd-dwc.o tc-dwc-g210.o
diff --git a/drivers/scsi/ufs/ufs-fault-injection.c b/drivers/scsi/ufs/ufs-fault-injection.c
new file mode 100644 (file)
index 0000000..7ac7c4e
--- /dev/null
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/kconfig.h>
+#include <linux/types.h>
+#include <linux/fault-inject.h>
+#include <linux/module.h>
+#include "ufs-fault-injection.h"
+
+static int ufs_fault_get(char *buffer, const struct kernel_param *kp);
+static int ufs_fault_set(const char *val, const struct kernel_param *kp);
+
+static const struct kernel_param_ops ufs_fault_ops = {
+       .get = ufs_fault_get,
+       .set = ufs_fault_set,
+};
+
+enum { FAULT_INJ_STR_SIZE = 80 };
+
+/*
+ * For more details about fault injection, please refer to
+ * Documentation/fault-injection/fault-injection.rst.
+ */
+static char g_trigger_eh_str[FAULT_INJ_STR_SIZE];
+module_param_cb(trigger_eh, &ufs_fault_ops, g_trigger_eh_str, 0644);
+MODULE_PARM_DESC(trigger_eh,
+       "Fault injection. trigger_eh=<interval>,<probability>,<space>,<times>");
+static DECLARE_FAULT_ATTR(ufs_trigger_eh_attr);
+
+static char g_timeout_str[FAULT_INJ_STR_SIZE];
+module_param_cb(timeout, &ufs_fault_ops, g_timeout_str, 0644);
+MODULE_PARM_DESC(timeout,
+       "Fault injection. timeout=<interval>,<probability>,<space>,<times>");
+static DECLARE_FAULT_ATTR(ufs_timeout_attr);
+
+static int ufs_fault_get(char *buffer, const struct kernel_param *kp)
+{
+       const char *fault_str = kp->arg;
+
+       return sysfs_emit(buffer, "%s\n", fault_str);
+}
+
+static int ufs_fault_set(const char *val, const struct kernel_param *kp)
+{
+       struct fault_attr *attr = NULL;
+
+       if (kp->arg == g_trigger_eh_str)
+               attr = &ufs_trigger_eh_attr;
+       else if (kp->arg == g_timeout_str)
+               attr = &ufs_timeout_attr;
+
+       if (WARN_ON_ONCE(!attr))
+               return -EINVAL;
+
+       if (!setup_fault_attr(attr, (char *)val))
+               return -EINVAL;
+
+       strlcpy(kp->arg, val, FAULT_INJ_STR_SIZE);
+
+       return 0;
+}
+
+bool ufs_trigger_eh(void)
+{
+       return should_fail(&ufs_trigger_eh_attr, 1);
+}
+
+bool ufs_fail_completion(void)
+{
+       return should_fail(&ufs_timeout_attr, 1);
+}
diff --git a/drivers/scsi/ufs/ufs-fault-injection.h b/drivers/scsi/ufs/ufs-fault-injection.h
new file mode 100644 (file)
index 0000000..6d0cd8e
--- /dev/null
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _UFS_FAULT_INJECTION_H
+#define _UFS_FAULT_INJECTION_H
+
+#include <linux/kconfig.h>
+#include <linux/types.h>
+
+#ifdef CONFIG_SCSI_UFS_FAULT_INJECTION
+bool ufs_trigger_eh(void);
+bool ufs_fail_completion(void);
+#else
+static inline bool ufs_trigger_eh(void)
+{
+       return false;
+}
+
+static inline bool ufs_fail_completion(void)
+{
+       return false;
+}
+#endif
+
+#endif /* _UFS_FAULT_INJECTION_H */
index 49cbb037552655f2038c65514bb38daefa860ab7..47a5085f16a9535a63e9aed1c85a3bf930b48582 100644 (file)
@@ -24,6 +24,7 @@
 #include "unipro.h"
 #include "ufs-sysfs.h"
 #include "ufs-debugfs.h"
+#include "ufs-fault-injection.h"
 #include "ufs_bsg.h"
 #include "ufshcd-crypto.h"
 #include "ufshpb.h"
@@ -2758,6 +2759,10 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
        ufshcd_send_command(hba, tag);
 out:
        up_read(&hba->clk_scaling_lock);
+
+       if (ufs_trigger_eh())
+               scsi_schedule_eh(hba->host);
+
        return err;
 }
 
@@ -5314,6 +5319,9 @@ static irqreturn_t ufshcd_transfer_req_compl(struct ufs_hba *hba,
            !(hba->quirks & UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR))
                ufshcd_reset_intr_aggr(hba);
 
+       if (ufs_fail_completion())
+               return IRQ_HANDLED;
+
        spin_lock_irqsave(&hba->outstanding_lock, flags);
        tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
        completed_reqs = ~tr_doorbell & hba->outstanding_reqs;