scsi: ufs: Add fault injection support

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>
This commit is contained in:
Bart Van Assche 2021-07-21 20:34:39 -07:00 committed by Martin K. Petersen
parent 73dc3c4ac7
commit c11a1ae9b8
5 changed files with 110 additions and 0 deletions

View 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.

View 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

View file

@ -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);
}

View file

@ -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 */

View 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;