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

Commit 861a4ae9 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "scsi: ufs-debug: add debugfs capability to collect query statistics"

parents 6ab227ad 7125ad6b
Loading
Loading
Loading
Loading
+94 −5
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;
@@ -1464,22 +1535,40 @@ void ufsdbg_add_debugfs(struct ufs_hba *hba)
		goto err_no_root;
	}

	hba->debugfs_files.stats_folder = debugfs_create_dir("stats",
					hba->debugfs_files.debugfs_root);
	if (!hba->debugfs_files.stats_folder) {
		dev_err(hba->dev,
			"%s: NULL stats_folder, exiting", __func__);
		goto err;
	}

	hba->debugfs_files.tag_stats =
		debugfs_create_file("tag_stats", S_IRUSR | S_IWUSR,
					   hba->debugfs_files.debugfs_root, hba,
					   hba->debugfs_files.stats_folder, hba,
					   &ufsdbg_tag_stats_fops);
	if (!hba->debugfs_files.tag_stats) {
		dev_err(hba->dev, "%s:  NULL tag stats file, exiting",
		dev_err(hba->dev, "%s:  NULL tag_stats file, exiting",
			__func__);
		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.debugfs_root, hba,
					   hba->debugfs_files.stats_folder, hba,
					   &ufsdbg_err_stats_fops);
	if (!hba->debugfs_files.err_stats) {
		dev_err(hba->dev, "%s:  NULL err stats file, exiting",
		dev_err(hba->dev, "%s:  NULL err_stats file, exiting",
			__func__);
		goto err;
	}
@@ -1561,7 +1650,7 @@ void ufsdbg_add_debugfs(struct ufs_hba *hba)

	hba->debugfs_files.req_stats =
		debugfs_create_file("req_stats", S_IRUSR | S_IWUSR,
			hba->debugfs_files.debugfs_root, hba,
			hba->debugfs_files.stats_folder, hba,
			&ufsdbg_req_stats_desc);
	if (!hba->debugfs_files.req_stats) {
		dev_err(hba->dev,
+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,
+6 −1
Original line number Diff line number Diff line
@@ -456,6 +456,7 @@ struct ufs_uic_err_reg_hist {
#ifdef CONFIG_DEBUG_FS
struct debugfs_files {
	struct dentry *debugfs_root;
	struct dentry *stats_folder;
	struct dentry *tag_stats;
	struct dentry *err_stats;
	struct dentry *show_hba;
@@ -466,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;
@@ -512,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.
@@ -530,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_ */