Loading drivers/scsi/ufs/ufs-debugfs.c +94 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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, Loading drivers/scsi/ufs/ufshcd.c +15 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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 Loading Loading @@ -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, Loading drivers/scsi/ufs/ufshcd.h +6 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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. Loading @@ -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; Loading include/uapi/scsi/ufs/ufs.h +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, Loading Loading @@ -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_ */ Loading
drivers/scsi/ufs/ufs-debugfs.c +94 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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, Loading
drivers/scsi/ufs/ufshcd.c +15 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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 Loading Loading @@ -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, Loading
drivers/scsi/ufs/ufshcd.h +6 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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. Loading @@ -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; Loading
include/uapi/scsi/ufs/ufs.h +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, Loading Loading @@ -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_ */