diff options
author | Subhash Jadavani <subhashj@codeaurora.org> | 2016-06-10 12:33:18 -0700 |
---|---|---|
committer | Kyle Yan <kyan@codeaurora.org> | 2016-06-24 15:05:01 -0700 |
commit | 73fa24503bf34047745bfd0602ec59f3ee2c74db (patch) | |
tree | ab5f539a06dd902ca213d872272483d5cee885df /drivers | |
parent | a26ae43d87f17437319b0a2f5e30b9f79ad18950 (diff) |
scsi: ufs-debugfs: add error state
This change adds support to allow user space query if low level UFS driver
has encountered any error or not, this state can be read/cleared via
debugfs.
Change-Id: I867a4621315108aff17be852cfaadcfa945566a7
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/ufs/ufs-debugfs.c | 47 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufs-debugfs.h | 6 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 16 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufshcd.h | 2 |
4 files changed, 69 insertions, 2 deletions
diff --git a/drivers/scsi/ufs/ufs-debugfs.c b/drivers/scsi/ufs/ufs-debugfs.c index 0f2f9bd91e02..f3b4b6c08571 100644 --- a/drivers/scsi/ufs/ufs-debugfs.c +++ b/drivers/scsi/ufs/ufs-debugfs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1433,6 +1433,41 @@ static const struct file_operations ufsdbg_reset_controller = { .write = ufsdbg_reset_controller_write, }; +static int ufsdbg_clear_err_state(void *data, u64 val) +{ + struct ufs_hba *hba = data; + + if (!hba) + return -EINVAL; + + /* clear the error state on any write attempt */ + hba->debugfs_files.err_occurred = false; + + return 0; +} + +static int ufsdbg_read_err_state(void *data, u64 *val) +{ + struct ufs_hba *hba = data; + + if (!hba) + return -EINVAL; + + *val = hba->debugfs_files.err_occurred ? 1 : 0; + + return 0; +} + +void ufsdbg_set_err_state(struct ufs_hba *hba) +{ + hba->debugfs_files.err_occurred = true; +} + +DEFINE_SIMPLE_ATTRIBUTE(ufsdbg_err_state, + ufsdbg_read_err_state, + ufsdbg_clear_err_state, + "%llu\n"); + void ufsdbg_add_debugfs(struct ufs_hba *hba) { char root_name[sizeof("ufshcd00")]; @@ -1594,6 +1629,16 @@ void ufsdbg_add_debugfs(struct ufs_hba *hba) goto err; } + hba->debugfs_files.err_state = + debugfs_create_file("err_state", S_IRUSR | S_IWUSR, + hba->debugfs_files.debugfs_root, hba, + &ufsdbg_err_state); + if (!hba->debugfs_files.err_state) { + dev_err(hba->dev, + "%s: failed create err_state debugfs entry", __func__); + goto err; + } + ufsdbg_setup_fault_injection(hba); ufshcd_vops_add_debugfs(hba, hba->debugfs_files.debugfs_root); diff --git a/drivers/scsi/ufs/ufs-debugfs.h b/drivers/scsi/ufs/ufs-debugfs.h index bf4d51ac8935..13848e8b72e0 100644 --- a/drivers/scsi/ufs/ufs-debugfs.h +++ b/drivers/scsi/ufs/ufs-debugfs.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -37,6 +37,7 @@ void ufsdbg_add_debugfs(struct ufs_hba *hba); void ufsdbg_remove_debugfs(struct ufs_hba *hba); void ufsdbg_pr_buf_to_std(struct ufs_hba *hba, int offset, int num_regs, char *str, void *priv); +void ufsdbg_set_err_state(struct ufs_hba *hba); #else static inline void ufsdbg_add_debugfs(struct ufs_hba *hba) { @@ -48,6 +49,9 @@ static inline void ufsdbg_pr_buf_to_std(struct ufs_hba *hba, int offset, int num_regs, char *str, void *priv) { } +void ufsdbg_set_err_state(struct ufs_hba *hba) +{ +} #endif #ifdef CONFIG_UFS_FAULT_INJECTION diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 40dcaa8f0eee..ad679e5d3f76 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -71,6 +71,7 @@ static int ufshcd_tag_req_type(struct request *rq) static void ufshcd_update_error_stats(struct ufs_hba *hba, int type) { + ufsdbg_set_err_state(hba); if (type < UFS_ERR_MAX) hba->ufs_stats.err_stats[type]++; } @@ -2143,6 +2144,9 @@ ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) else ret = -ETIMEDOUT; + if (ret) + ufsdbg_set_err_state(hba); + spin_lock_irqsave(hba->host->host_lock, flags); hba->active_uic_cmd = NULL; spin_unlock_irqrestore(hba->host->host_lock, flags); @@ -2842,6 +2846,9 @@ static int ufshcd_wait_for_dev_cmd(struct ufs_hba *hba, ufshcd_outstanding_req_clear(hba, lrbp->task_tag); } + if (err) + ufsdbg_set_err_state(hba); + return err; } @@ -3874,6 +3881,9 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd) ret = (status != PWR_OK) ? status : -1; } out: + if (ret) + ufsdbg_set_err_state(hba); + ufshcd_save_tstamp_of_last_dme_cmd(hba); spin_lock_irqsave(hba->host->host_lock, flags); hba->active_uic_cmd = NULL; @@ -4947,6 +4957,11 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) ocs == OCS_MISMATCH_DATA_BUF_SIZE); ufshcd_print_trs(hba, 1 << lrbp->task_tag, print_prdt); } + + if ((host_byte(result) == DID_ERROR) || + (host_byte(result) == DID_ABORT)) + ufsdbg_set_err_state(hba); + return result; } @@ -5532,6 +5547,7 @@ static void ufshcd_err_handler(struct work_struct *work) hba = container_of(work, struct ufs_hba, eh_work); + ufsdbg_set_err_state(hba); pm_runtime_get_sync(hba->dev); ufshcd_hold_all(hba); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 1ccda45743d6..a4ee3726edb0 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -543,6 +543,8 @@ struct debugfs_files { u32 dme_local_attr_id; u32 dme_peer_attr_id; struct dentry *reset_controller; + struct dentry *err_state; + bool err_occurred; #ifdef CONFIG_UFS_FAULT_INJECTION struct dentry *err_inj_scenario; struct dentry *err_inj_stats; |