diff options
| author | Yaniv Gardi <ygardi@codeaurora.org> | 2015-06-09 10:22:26 +0300 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 10:59:56 -0700 |
| commit | 966284567f964015aa537f487db24eceef951aba (patch) | |
| tree | f6c78f0fe65a4a305eda1998414f84b74eff1477 | |
| parent | 4d53145d9793da4f24c0923de350303b2f09d3fb (diff) | |
scsi: ufs-debug: add debugfs capability to collect query statistics
This patch adds a new debugfs capability to collect query statistics.
It counts how many times each IDN (index) was sent to device for each
query opcode.
A usage example:
1. to clean current query statistics:
echo 1 > /d/ufshcd0/stats/query_stats
2. to dump the current query statistics:
cat /d/ufshcd0/stats/query_stats
This capability is always on, and can not be turned off.
Change-Id: I46ec405aae480c0dc161dca015b407bde6335cf7
Signed-off-by: Yaniv Gardi <ygardi@codeaurora.org>
| -rw-r--r-- | drivers/scsi/ufs/ufs-debugfs.c | 81 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 15 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.h | 6 | ||||
| -rw-r--r-- | include/uapi/scsi/ufs/ufs.h | 3 |
4 files changed, 104 insertions, 1 deletions
diff --git a/drivers/scsi/ufs/ufs-debugfs.c b/drivers/scsi/ufs/ufs-debugfs.c index 03cda3099673..bdb12352e8c4 100644 --- a/drivers/scsi/ufs/ufs-debugfs.c +++ b/drivers/scsi/ufs/ufs-debugfs.c @@ -678,6 +678,77 @@ static const struct file_operations ufsdbg_tag_stats_fops = { .write = ufsdbg_tag_stats_write, }; +static int ufsdbg_query_stats_show(struct seq_file *file, void *data) +{ + struct ufs_hba *hba = (struct ufs_hba *)file->private; + struct ufs_stats *ufs_stats = &hba->ufs_stats; + int i, j; + static const char *opcode_name[UPIU_QUERY_OPCODE_MAX] = { + "QUERY_OPCODE_NOP:", + "QUERY_OPCODE_READ_DESC:", + "QUERY_OPCODE_WRITE_DESC:", + "QUERY_OPCODE_READ_ATTR:", + "QUERY_OPCODE_WRITE_ATTR:", + "QUERY_OPCODE_READ_FLAG:", + "QUERY_OPCODE_SET_FLAG:", + "QUERY_OPCODE_CLEAR_FLAG:", + "QUERY_OPCODE_TOGGLE_FLAG:", + }; + + seq_puts(file, "\n"); + seq_puts(file, "The following table shows how many TIMES each IDN was sent to device for each QUERY OPCODE:\n"); + seq_puts(file, "\n"); + + for (i = 0; i < UPIU_QUERY_OPCODE_MAX; i++) { + seq_printf(file, "%-30s", opcode_name[i]); + + for (j = 0; j < MAX_QUERY_IDN; j++) { + /* + * we would like to print only the non-zero data, + * (non-zero number of times that IDN was sent + * to the device per opcode). There is no + * importance to the "table structure" of the output. + */ + if (ufs_stats->query_stats_arr[i][j]) + seq_printf(file, "IDN 0x%02X: %d,\t", j, + ufs_stats->query_stats_arr[i][j]); + } + seq_puts(file, "\n"); + } + + return 0; +} + +static int ufsdbg_query_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, ufsdbg_query_stats_show, inode->i_private); +} + +static ssize_t ufsdbg_query_stats_write(struct file *filp, + const char __user *ubuf, size_t cnt, + loff_t *ppos) +{ + struct ufs_hba *hba = filp->f_mapping->host->i_private; + struct ufs_stats *ufs_stats = &hba->ufs_stats; + int i, j; + + mutex_lock(&hba->dev_cmd.lock); + + for (i = 0; i < UPIU_QUERY_OPCODE_MAX; i++) + for (j = 0; j < MAX_QUERY_IDN; j++) + ufs_stats->query_stats_arr[i][j] = 0; + + mutex_unlock(&hba->dev_cmd.lock); + + return cnt; +} + +static const struct file_operations ufsdbg_query_stats_fops = { + .open = ufsdbg_query_stats_open, + .read = seq_read, + .write = ufsdbg_query_stats_write, +}; + static int ufsdbg_err_stats_show(struct seq_file *file, void *data) { struct ufs_hba *hba = (struct ufs_hba *)file->private; @@ -1482,6 +1553,16 @@ void ufsdbg_add_debugfs(struct ufs_hba *hba) goto err; } + hba->debugfs_files.query_stats = + debugfs_create_file("query_stats", S_IRUSR | S_IWUSR, + hba->debugfs_files.stats_folder, hba, + &ufsdbg_query_stats_fops); + if (!hba->debugfs_files.query_stats) { + dev_err(hba->dev, "%s: NULL query_stats file, exiting", + __func__); + goto err; + } + hba->debugfs_files.err_stats = debugfs_create_file("err_stats", S_IRUSR | S_IWUSR, hba->debugfs_files.stats_folder, hba, diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index cc567cd7572e..423d32e66835 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -136,6 +136,13 @@ static void update_req_stats(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) hba->ufs_stats.req_stats[rq_type].min = delta; } +static void +ufshcd_update_query_stats(struct ufs_hba *hba, enum query_opcode opcode, u8 idn) +{ + if (opcode < UPIU_QUERY_OPCODE_MAX && idn < MAX_QUERY_IDN) + hba->ufs_stats.query_stats_arr[opcode][idn]++; +} + #else static inline void ufshcd_update_tag_stats(struct ufs_hba *hba, int tag) { @@ -154,6 +161,12 @@ static inline void update_req_stats(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) { } + +static inline +void ufshcd_update_query_stats(struct ufs_hba *hba, + enum query_opcode opcode, u8 idn) +{ +} #endif #define UFSHCD_REQ_SENSE_SIZE 18 @@ -2835,6 +2848,8 @@ static inline void ufshcd_init_query(struct ufs_hba *hba, (*request)->upiu_req.idn = idn; (*request)->upiu_req.index = index; (*request)->upiu_req.selector = selector; + + ufshcd_update_query_stats(hba, opcode, idn); } static int ufshcd_query_flag_retry(struct ufs_hba *hba, diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index e0a62791c0dd..ad454a3cc16e 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -475,6 +475,7 @@ struct debugfs_files { struct dentry *dme_peer_read; struct dentry *dbg_print_en; struct dentry *req_stats; + struct dentry *query_stats; u32 dme_local_attr_id; u32 dme_peer_attr_id; struct dentry *reset_controller; @@ -521,7 +522,8 @@ struct ufshcd_req_stat { * @tag_stats: pointer to tag statistic counters * @q_depth: current amount of busy slots * @err_stats: counters to keep track of various errors - * @req_stat: request handling time statistics per request type + * @req_stats: request handling time statistics per request type + * @query_stats_arr: array that holds query statistics * @hibern8_exit_cnt: Counter to keep track of number of exits, * reset this after link-startup. * @last_hibern8_exit_tstamp: Set time after the hibern8 exit. @@ -539,6 +541,8 @@ struct ufs_stats { int q_depth; int err_stats[UFS_ERR_MAX]; struct ufshcd_req_stat req_stats[TS_NUM_STATS]; + int query_stats_arr[UPIU_QUERY_OPCODE_MAX][MAX_QUERY_IDN]; + #endif u32 hibern8_exit_cnt; ktime_t last_hibern8_exit_tstamp; diff --git a/include/uapi/scsi/ufs/ufs.h b/include/uapi/scsi/ufs/ufs.h index 894ea453737f..4b9a0049639d 100644 --- a/include/uapi/scsi/ufs/ufs.h +++ b/include/uapi/scsi/ufs/ufs.h @@ -1,6 +1,8 @@ #ifndef UAPI_UFS_H_ #define UAPI_UFS_H_ +#define MAX_QUERY_IDN 0x12 + /* Flag idn for Query Requests*/ enum flag_idn { QUERY_FLAG_IDN_FDEVICEINIT = 0x01, @@ -62,5 +64,6 @@ enum query_opcode { UPIU_QUERY_OPCODE_SET_FLAG = 0x6, UPIU_QUERY_OPCODE_CLEAR_FLAG = 0x7, UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8, + UPIU_QUERY_OPCODE_MAX, }; #endif /* UAPI_UFS_H_ */ |
