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 ee65052..432df76 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 cce9b39..c407da9 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 49cbb03..47a5085 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;