summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYaniv Gardi <ygardi@codeaurora.org>2015-06-09 10:22:26 +0300
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 10:59:56 -0700
commit966284567f964015aa537f487db24eceef951aba (patch)
treef6c78f0fe65a4a305eda1998414f84b74eff1477
parent4d53145d9793da4f24c0923de350303b2f09d3fb (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.c81
-rw-r--r--drivers/scsi/ufs/ufshcd.c15
-rw-r--r--drivers/scsi/ufs/ufshcd.h6
-rw-r--r--include/uapi/scsi/ufs/ufs.h3
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_ */