diff options
| -rw-r--r-- | drivers/scsi/ufs/Kconfig | 12 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 179 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.h | 24 |
3 files changed, 197 insertions, 18 deletions
diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig index b3ed361eff26..fb2f2159c0e1 100644 --- a/drivers/scsi/ufs/Kconfig +++ b/drivers/scsi/ufs/Kconfig @@ -106,3 +106,15 @@ config SCSI_UFS_TEST The UFS unit-tests register as a block device test utility to the test-iosched and will be initiated when the test-iosched will be chosen to be the active I/O scheduler. + +config SCSI_UFSHCD_CMD_LOGGING + bool "Universal Flash Storage host controller driver layer command logging support" + depends on SCSI_UFSHCD + help + This selects the UFS host controller driver layer command logging. + UFS host controller driver layer command logging records all the + command information sent from UFS host controller for debugging + purpose. + + Select this if you want above mentioned debug information captured. + If unsure, say N. diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index c5393d517432..7e2f5e06c036 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -516,14 +516,145 @@ static inline void ufshcd_remove_non_printable(char *val) *val = ' '; } +#define UFSHCD_MAX_CMD_LOGGING 100 + #ifdef CONFIG_TRACEPOINTS -static void ufshcd_add_command_trace(struct ufs_hba *hba, - unsigned int tag, const char *str) +static inline void ufshcd_add_command_trace(struct ufs_hba *hba, + struct ufshcd_cmd_log_entry *entry, u8 opcode) +{ + if (trace_ufshcd_command_enabled()) { + u32 intr = ufshcd_readl(hba, REG_INTERRUPT_STATUS); + + trace_ufshcd_command(dev_name(hba->dev), entry->str, entry->tag, + entry->doorbell, entry->transfer_len, intr, + entry->lba, opcode); + } +} +#else +static inline void ufshcd_add_command_trace(struct ufs_hba *hba, + struct ufshcd_cmd_log_entry *entry, u8 opcode) +{ +} +#endif + +#ifdef CONFIG_SCSI_UFSHCD_CMD_LOGGING +static void ufshcd_cmd_log_init(struct ufs_hba *hba) +{ + /* Allocate log entries */ + if (!hba->cmd_log.entries) { + hba->cmd_log.entries = kzalloc(UFSHCD_MAX_CMD_LOGGING * + sizeof(struct ufshcd_cmd_log_entry), GFP_KERNEL); + if (!hba->cmd_log.entries) + return; + dev_dbg(hba->dev, "%s: cmd_log.entries initialized\n", + __func__); + } +} + +static void __ufshcd_cmd_log(struct ufs_hba *hba, char *str, char *cmd_type, + unsigned int tag, u8 cmd_id, u8 idn, u8 lun, + sector_t lba, int transfer_len, u8 opcode) +{ + struct ufshcd_cmd_log_entry *entry; + + if (!hba->cmd_log.entries) + return; + + entry = &hba->cmd_log.entries[hba->cmd_log.pos]; + entry->lun = lun; + entry->str = str; + entry->cmd_type = cmd_type; + entry->cmd_id = cmd_id; + entry->lba = lba; + entry->transfer_len = transfer_len; + entry->idn = idn; + entry->doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); + entry->tag = tag; + entry->tstamp = ktime_get(); + entry->outstanding_reqs = hba->outstanding_reqs; + entry->seq_num = hba->cmd_log.seq_num; + hba->cmd_log.seq_num++; + hba->cmd_log.pos = + (hba->cmd_log.pos + 1) % UFSHCD_MAX_CMD_LOGGING; + + ufshcd_add_command_trace(hba, entry, opcode); +} + +static void ufshcd_cmd_log(struct ufs_hba *hba, char *str, char *cmd_type, + unsigned int tag, u8 cmd_id, u8 idn) +{ + __ufshcd_cmd_log(hba, str, cmd_type, tag, cmd_id, idn, + 0xff, (sector_t)-1, -1, -1); +} + +static void ufshcd_dme_cmd_log(struct ufs_hba *hba, char *str, u8 cmd_id) +{ + ufshcd_cmd_log(hba, str, "dme", 0xff, cmd_id, 0xff); +} + +static void ufshcd_cmd_log_print(struct ufs_hba *hba) +{ + int i; + int pos; + struct ufshcd_cmd_log_entry *p; + + if (!hba->cmd_log.entries) + return; + + pos = hba->cmd_log.pos; + for (i = 0; i < UFSHCD_MAX_CMD_LOGGING; i++) { + p = &hba->cmd_log.entries[pos]; + pos = (pos + 1) % UFSHCD_MAX_CMD_LOGGING; + + if (ktime_to_us(p->tstamp)) { + pr_err("%s: %s: seq_no=%u lun=0x%x cmd_id=0x%02x lba=0x%llx txfer_len=%d tag=%u, doorbell=0x%x outstanding=0x%x idn=%d time=%lld us\n", + p->cmd_type, p->str, p->seq_num, + p->lun, p->cmd_id, (unsigned long long)p->lba, + p->transfer_len, p->tag, p->doorbell, + p->outstanding_reqs, p->idn, + ktime_to_us(p->tstamp)); + usleep_range(1000, 1100); + } + } +} +#else +static void ufshcd_cmd_log_init(struct ufs_hba *hba) +{ +} + +static void __ufshcd_cmd_log(struct ufs_hba *hba, char *str, char *cmd_type, + unsigned int tag, u8 cmd_id, u8 idn, u8 lun, + sector_t lba, int transfer_len, u8 opcode) +{ + struct ufshcd_cmd_log_entry entry; + + entry.str = str; + entry.lba = lba; + entry.transfer_len = transfer_len; + entry.doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); + entry.tag = tag; + + ufshcd_add_command_trace(hba, &entry, opcode); +} + +static void ufshcd_dme_cmd_log(struct ufs_hba *hba, char *str, u8 cmd_id) +{ +} + +static void ufshcd_cmd_log_print(struct ufs_hba *hba) +{ +} +#endif + +#ifdef CONFIG_TRACEPOINTS +static inline void ufshcd_cond_add_cmd_trace(struct ufs_hba *hba, + unsigned int tag, const char *str) { - sector_t lba = -1; - u8 opcode = 0; - u32 intr, doorbell; struct ufshcd_lrb *lrbp; + char *cmd_type; + u8 opcode = 0; + u8 cmd_id, idn = 0; + sector_t lba = -1; int transfer_len = -1; lrbp = &hba->lrb[tag]; @@ -537,23 +668,28 @@ static void ufshcd_add_command_trace(struct ufs_hba *hba, */ if (lrbp->cmd->request && lrbp->cmd->request->bio) lba = - lrbp->cmd->request->bio->bi_iter.bi_sector; + lrbp->cmd->request->bio->bi_iter.bi_sector; transfer_len = be32_to_cpu( lrbp->ucd_req_ptr->sc.exp_data_transfer_len); } } - intr = ufshcd_readl(hba, REG_INTERRUPT_STATUS); - doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); - trace_ufshcd_command(dev_name(hba->dev), str, tag, - doorbell, transfer_len, intr, lba, opcode); -} + if (lrbp->command_type == UTP_CMD_TYPE_SCSI) { + cmd_type = "scsi"; + cmd_id = (u8)(*lrbp->cmd->cmnd); + } else if (lrbp->command_type == UTP_CMD_TYPE_DEV_MANAGE) { + if (hba->dev_cmd.type == DEV_CMD_TYPE_NOP) { + cmd_type = "nop"; + cmd_id = 0; + } else if (hba->dev_cmd.type == DEV_CMD_TYPE_QUERY) { + cmd_type = "query"; + cmd_id = hba->dev_cmd.query.request.upiu_req.opcode; + idn = hba->dev_cmd.query.request.upiu_req.idn; + } + } -static inline void ufshcd_cond_add_cmd_trace(struct ufs_hba *hba, - unsigned int tag, const char *str) -{ - if (trace_ufshcd_command_enabled()) - ufshcd_add_command_trace(hba, tag, str); + __ufshcd_cmd_log(hba, (char *) str, cmd_type, tag, cmd_id, idn, + lrbp->lun, lba, transfer_len, opcode); } #else static inline void ufshcd_cond_add_cmd_trace(struct ufs_hba *hba, @@ -2263,6 +2399,7 @@ ufshcd_dispatch_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) hba->active_uic_cmd = uic_cmd; + ufshcd_dme_cmd_log(hba, "send", hba->active_uic_cmd->command); /* Write Args */ ufshcd_writel(hba, uic_cmd->argument1, REG_UIC_COMMAND_ARG_1); ufshcd_writel(hba, uic_cmd->argument2, REG_UIC_COMMAND_ARG_2); @@ -2296,6 +2433,8 @@ ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) if (ret) ufsdbg_set_err_state(hba); + ufshcd_dme_cmd_log(hba, "cmp1", hba->active_uic_cmd->command); + spin_lock_irqsave(hba->host->host_lock, flags); hba->active_uic_cmd = NULL; spin_unlock_irqrestore(hba->host->host_lock, flags); @@ -4113,6 +4252,8 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd) cmd->command, status); ret = (status != PWR_OK) ? status : -1; } + ufshcd_dme_cmd_log(hba, "cmp2", hba->active_uic_cmd->command); + out: if (ret) { ufsdbg_set_err_state(hba); @@ -5421,7 +5562,7 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba, } else if (lrbp->command_type == UTP_CMD_TYPE_DEV_MANAGE) { if (hba->dev_cmd.complete) { ufshcd_cond_add_cmd_trace(hba, index, - "dev_complete"); + "dcmp"); complete(hba->dev_cmd.complete); } } @@ -5943,6 +6084,7 @@ static void ufshcd_err_handler(struct work_struct *work) ufshcd_print_host_state(hba); ufshcd_print_pwr_info(hba); ufshcd_print_tmrs(hba, hba->outstanding_tasks); + ufshcd_cmd_log_print(hba); spin_lock_irqsave(hba->host->host_lock, flags); } } @@ -6449,6 +6591,7 @@ static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd) hba = shost_priv(host); tag = cmd->request->tag; + ufshcd_cmd_log_print(hba); lrbp = &hba->lrb[tag]; err = ufshcd_issue_tm_cmd(hba, lrbp->lun, 0, UFS_LOGICAL_RESET, &resp); if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) { @@ -9809,6 +9952,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) */ ufshcd_set_ufs_dev_active(hba); + ufshcd_cmd_log_init(hba); + async_schedule(ufshcd_async_scan, hba); ufsdbg_add_debugfs(hba); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index c34a998aac17..12d8f7ff53ed 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -519,7 +519,7 @@ struct ufs_init_prefetch { u32 icc_level; }; -#define UIC_ERR_REG_HIST_LENGTH 8 +#define UIC_ERR_REG_HIST_LENGTH 20 /** * struct ufs_uic_err_reg_hist - keeps history of uic errors * @pos: index to indicate cyclic buffer position @@ -641,6 +641,27 @@ struct ufs_stats { UFSHCD_DBG_PRINT_TMRS_EN | UFSHCD_DBG_PRINT_PWR_EN | \ UFSHCD_DBG_PRINT_HOST_STATE_EN) +struct ufshcd_cmd_log_entry { + char *str; /* context like "send", "complete" */ + char *cmd_type; /* "scsi", "query", "nop", "dme" */ + u8 lun; + u8 cmd_id; + sector_t lba; + int transfer_len; + u8 idn; /* used only for query idn */ + u32 doorbell; + u32 outstanding_reqs; + u32 seq_num; + unsigned int tag; + ktime_t tstamp; +}; + +struct ufshcd_cmd_log { + struct ufshcd_cmd_log_entry *entries; + int pos; + u32 seq_num; +}; + /** * struct ufs_hba - per adapter private structure * @mmio_base: UFSHCI base register address @@ -856,6 +877,7 @@ struct ufs_hba { struct ufs_clk_gating clk_gating; struct ufs_hibern8_on_idle hibern8_on_idle; + struct ufshcd_cmd_log cmd_log; /* Control to enable/disable host capabilities */ u32 caps; |
