> From: Sujit Reddy Thumma <sthumma@xxxxxxxxxxxxxx> > > Use fault-injection framework to simulate error conditions > in the controller and verify error handling mechanisms > implemented in UFS host controller driver. > > This is used only during development and hence > guarded by CONFIG_UFS_FAULT_INJECTION debug config option. > > Signed-off-by: Sujit Reddy Thumma <sthumma@xxxxxxxxxxxxxx> > --- > drivers/scsi/ufs/ufs-debugfs.c | 140 > +++++++++++++++++++++++++++++++++++++++++ > drivers/scsi/ufs/ufs-debugfs.h | 4 ++ > drivers/scsi/ufs/ufshcd.c | 2 + > drivers/scsi/ufs/ufshcd.h | 5 ++ > lib/Kconfig.debug | 14 +++++ > 5 files changed, 165 insertions(+) > > diff --git a/drivers/scsi/ufs/ufs-debugfs.c > b/drivers/scsi/ufs/ufs-debugfs.c > index d1eb4f8..53dcb00 100644 > --- a/drivers/scsi/ufs/ufs-debugfs.c > +++ b/drivers/scsi/ufs/ufs-debugfs.c > @@ -17,6 +17,7 @@ > * > */ > > +#include <linux/random.h> > #include "ufs-debugfs.h" > #include "unipro.h" > > @@ -41,6 +42,143 @@ struct desc_field_offset { > } while (0) > #define DOORBELL_CLR_TOUT_US (1000 * 1000) /* 1 sec */ > > +#ifdef CONFIG_UFS_FAULT_INJECTION > + > +#define INJECT_COMMAND_HANG (0x0) > + > +static DECLARE_FAULT_ATTR(fail_default_attr); > +static char *fail_request; > +module_param(fail_request, charp, 0); > + > +static bool inject_fatal_err_tr(struct ufs_hba *hba, u8 ocs_err) > +{ > + int tag; > + > + tag = find_first_bit(&hba->outstanding_reqs, hba->nutrs); > + if (tag == hba->nutrs) > + return 0; > + > + ufshcd_writel(hba, ~(1 << tag), REG_UTP_TRANSFER_REQ_LIST_CLEAR); > + (&hba->lrb[tag])->utr_descriptor_ptr->header.dword_2 = > + > cpu_to_be32(ocs_err); > + > + /* fatal error injected */ > + return 1; > +} > + > +static bool inject_fatal_err_tm(struct ufs_hba *hba, u8 ocs_err) > +{ > + int tag; > + > + tag = find_first_bit(&hba->outstanding_tasks, hba->nutmrs); > + if (tag == hba->nutmrs) > + return 0; > + > + ufshcd_writel(hba, ~(1 << tag), REG_UTP_TASK_REQ_LIST_CLEAR); > + (&hba->utmrdl_base_addr[tag])->header.dword_2 = > + cpu_to_be32(ocs_err); > + > + /* fatal error injected */ > + return 1; > +} > + > +static bool inject_cmd_hang_tr(struct ufs_hba *hba) > +{ > + int tag; > + > + tag = find_first_bit(&hba->outstanding_reqs, hba->nutrs); > + if (tag == hba->nutrs) > + return 0; > + > + __clear_bit(tag, &hba->outstanding_reqs); > + hba->lrb[tag].cmd = NULL; > + __clear_bit(tag, &hba->lrb_in_use); > + > + /* command hang injected */ > + return 1; > +} > + > +static int inject_cmd_hang_tm(struct ufs_hba *hba) > +{ > + int tag; > + > + tag = find_first_bit(&hba->outstanding_tasks, hba->nutmrs); > + if (tag == hba->nutmrs) > + return 0; > + > + __clear_bit(tag, &hba->outstanding_tasks); > + __clear_bit(tag, &hba->tm_slots_in_use); > + > + /* command hang injected */ > + return 1; > +} > + > +void ufsdbg_fail_request(struct ufs_hba *hba, u32 *intr_status) > +{ > + u8 ocs_err; > + static const u32 errors[] = { > + CONTROLLER_FATAL_ERROR, > + SYSTEM_BUS_FATAL_ERROR, > + INJECT_COMMAND_HANG, > + }; > + > + if (!should_fail(&hba->debugfs_files.fail_attr, 1)) > + goto out; > + > + *intr_status = errors[prandom_u32() % ARRAY_SIZE(errors)]; > + dev_info(hba->dev, "%s: fault-inject error: 0x%x\n", > + __func__, *intr_status); > + > + switch (*intr_status) { > + case CONTROLLER_FATAL_ERROR: /* fall through */ > + ocs_err = OCS_FATAL_ERROR; > + goto set_ocs; > + case SYSTEM_BUS_FATAL_ERROR: > + ocs_err = OCS_INVALID_CMD_TABLE_ATTR; > +set_ocs: > + if (!inject_fatal_err_tr(hba, ocs_err)) > + if (!inject_fatal_err_tm(hba, ocs_err)) > + *intr_status = 0; > + break; > + case INJECT_COMMAND_HANG: > + if (!inject_cmd_hang_tr(hba)) > + inject_cmd_hang_tm(hba); > + break; > + default: > + BUG(); > + /* some configurations ignore panics caused by BUG() */ > + break; > + } > +out: > + return; > +} > + > +static void ufsdbg_setup_fault_injection(struct ufs_hba *hba) > +{ > + hba->debugfs_files.fail_attr = fail_default_attr; > + > + if (fail_request) > + setup_fault_attr(&hba->debugfs_files.fail_attr, > fail_request); > + > + /* suppress dump stack everytime failure is injected */ > + hba->debugfs_files.fail_attr.verbose = 0; > + > + if (IS_ERR(fault_create_debugfs_attr("inject_fault", > + hba->debugfs_files.debugfs_root, > + &hba->debugfs_files.fail_attr))) > + dev_err(hba->dev, "%s: failed to create debugfs entry\n", > + __func__); > +} > +#else > +void ufsdbg_fail_request(struct ufs_hba *hba, u32 *intr_status) > +{ > +} > + > +static void ufsdbg_setup_fault_injection(struct ufs_hba *hba) > +{ > +} > +#endif /* CONFIG_UFS_FAULT_INJECTION */ > + > #define BUFF_LINE_CAPACITY 16 > #define TAB_CHARS 8 > > @@ -885,6 +1023,8 @@ void ufsdbg_add_debugfs(struct ufs_hba *hba) > goto err; > } > > + ufsdbg_setup_fault_injection(hba); > + > return; > > err: > diff --git a/drivers/scsi/ufs/ufs-debugfs.h > b/drivers/scsi/ufs/ufs-debugfs.h > index 7ed308d..54c68f4 100644 > --- a/drivers/scsi/ufs/ufs-debugfs.h > +++ b/drivers/scsi/ufs/ufs-debugfs.h > @@ -26,6 +26,7 @@ > #ifdef CONFIG_DEBUG_FS > void ufsdbg_add_debugfs(struct ufs_hba *hba); > void ufsdbg_remove_debugfs(struct ufs_hba *hba); > +void ufsdbg_fail_request(struct ufs_hba *hba, u32 *intr_status); > #else > static inline void ufsdbg_add_debugfs(struct ufs_hba *hba) > { > @@ -33,6 +34,9 @@ static inline void ufsdbg_add_debugfs(struct ufs_hba > *hba) > static inline void ufsdbg_remove_debugfs(struct ufs_hba *hba) > { > } > +static inline void ufsdbg_fail_request(struct ufs_hba *hba, u32 > *intr_status) > +{ > +} > #endif > > #endif /* End of Header */ > diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c > index ae934f2..99c1a81 100644 > --- a/drivers/scsi/ufs/ufshcd.c > +++ b/drivers/scsi/ufs/ufshcd.c > @@ -4003,6 +4003,8 @@ static void ufshcd_tmc_handler(struct ufs_hba *hba) > */ > static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) > { > + ufsdbg_fail_request(hba, &intr_status); > + > hba->errors = UFSHCD_ERROR_MASK & intr_status; > if (hba->errors) > ufshcd_check_errors(hba); > diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h > index d9eb2ca..b065295 100644 > --- a/drivers/scsi/ufs/ufshcd.h > +++ b/drivers/scsi/ufs/ufshcd.h > @@ -64,6 +64,8 @@ > #include <scsi/scsi_dbg.h> > #include <scsi/scsi_eh.h> > > +#include <linux/fault-inject.h> > + > #include "ufs.h" > #include "ufshci.h" > > @@ -283,6 +285,9 @@ struct debugfs_files { > struct dentry *dme_peer_read; > u32 dme_local_attr_id; > u32 dme_peer_attr_id; > +#ifdef CONFIG_UFS_FAULT_INJECTION > + struct fault_attr fail_attr; > +#endif > }; > > /* tag stats statistics types */ > diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug > index 5f2ce61..3fc79e7 100644 > --- a/lib/Kconfig.debug > +++ b/lib/Kconfig.debug > @@ -1432,6 +1432,20 @@ config FAIL_MMC_REQUEST > and to test how the mmc host driver handles retries from > the block device. > > +config UFS_FAULT_INJECTION > + bool "Fault-injection capability for UFS IO" > + select DEBUG_FS > + depends on FAULT_INJECTION && SCSI_UFSHCD > + help > + Provide fault-injection capability for UFS IO. > + This will make the UFS host controller driver to randomly > + abort ongoing commands in the host controller, update OCS > + field according to the injected fatal error and can also > + forcefully hang the command indefinitely till upper layer > + timeout occurs. This is useful to test error handling in > + the UFS contoller driver and test how the driver handles > + the retries from block/SCSI mid layer. > + > config FAULT_INJECTION_DEBUG_FS > bool "Debugfs entries for fault-injection capabilities" > depends on FAULT_INJECTION && SYSFS && DEBUG_FS > -- > Qualcomm Israel, on behalf of Qualcomm Innovation Center, Inc. > The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, > a Linux Foundation Collaborative Project > > -- > To unsubscribe from this list: send the line "unsubscribe linux-scsi" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > Reviewed-by: Dov Levenglick <dovl@xxxxxxxxxxxxxx> QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html