Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7125ad6b authored by Yaniv Gardi's avatar Yaniv Gardi
Browse files

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: default avatarYaniv Gardi <ygardi@codeaurora.org>
parent faf66608
Loading
Loading
Loading
Loading
+81 −0
Original line number Diff line number Diff line
@@ -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,
+15 −0
Original line number Diff line number Diff line
@@ -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,
+5 −1
Original line number Diff line number Diff line
@@ -467,6 +467,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;
@@ -513,7 +514,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.
@@ -531,6 +533,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;
+3 −0
Original line number Diff line number Diff line
#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_ */