diff --git a/drivers/scsi/ufs/debugfs.c b/drivers/scsi/ufs/debugfs.c index bdafdc85405a..d8c3bcda9ca8 100644 --- a/drivers/scsi/ufs/debugfs.c +++ b/drivers/scsi/ufs/debugfs.c @@ -169,15 +169,17 @@ static void ufsdbg_setup_fault_injection(struct ufs_hba *hba) #endif /* CONFIG_UFS_FAULT_INJECTION */ #define BUFF_LINE_CAPACITY 16 +#define TAB_CHARS 8 static int ufsdbg_tag_stats_show(struct seq_file *file, void *data) { struct ufs_hba *hba = (struct ufs_hba *)file->private; struct ufs_stats *ufs_stats; - int i; + int i, j; int max_depth; bool is_tag_empty = true; unsigned long flags; + char *sep = " | * | "; if (!hba) goto exit; @@ -186,27 +188,44 @@ static int ufsdbg_tag_stats_show(struct seq_file *file, void *data) if (!ufs_stats->enabled) { pr_debug("%s: ufs statistics are disabled\n", __func__); + seq_puts(file, "ufs statistics are disabled"); goto exit; } max_depth = hba->nutrs; - pr_debug("%s: UFS tag statistics:\n", __func__); - pr_debug("%s: Max tagged command queue depth is %d", - __func__, max_depth); - spin_lock_irqsave(hba->host->host_lock, flags); + /* Header */ + seq_printf(file, " Tag Stat\t\t%s Queue Fullness\n", sep); + for (i = 0; i < TAB_CHARS * (TS_NUM_STATS + 4); i++) { + seq_puts(file, "-"); + if (i == (TAB_CHARS * 3 - 1)) + seq_puts(file, sep); + } + seq_printf(file, + "\n #\tnum uses\t%s\t #\tAll\t Read\t Write\t Urgent\t Flush\n", + sep); - for (i = 0 ; i < max_depth ; ++i) { - if (hba->ufs_stats.tag_stats[i] != 0) { - is_tag_empty = false; - seq_printf(file, - "%s: Dispatched tag %d - %llu times\n", - __func__, i, ufs_stats->tag_stats[i]); + /* values */ + for (i = 0; i < max_depth; i++) { + if (ufs_stats->tag_stats[i][0] <= 0 && + ufs_stats->tag_stats[i][1] <= 0 && + ufs_stats->tag_stats[i][2] <= 0 && + ufs_stats->tag_stats[i][3] <= 0 && + ufs_stats->tag_stats[i][4] <= 0) + continue; - pr_debug("%s: Dispatched tag %d - %llu times\n", - __func__, i, ufs_stats->tag_stats[i]); + is_tag_empty = false; + seq_printf(file, " %d\t ", i); + for (j = 0; j < TS_NUM_STATS; j++) { + seq_printf(file, "%llu\t ", ufs_stats->tag_stats[i][j]); + if (j == 0) + seq_printf(file, "\t%s\t %d\t%llu\t ", sep, i, + ufs_stats->tag_stats[i][j+1] + + ufs_stats->tag_stats[i][j+2] + + ufs_stats->tag_stats[i][j+3]); } + seq_puts(file, "\n"); } spin_unlock_irqrestore(hba->host->host_lock, flags); @@ -229,7 +248,7 @@ static ssize_t ufsdbg_tag_stats_write(struct file *filp, struct ufs_hba *hba = filp->f_mapping->host->i_private; struct ufs_stats *ufs_stats; int val = 0; - int ret; + int ret, bit = 0; unsigned long flags; ret = kstrtoint_from_user(ubuf, cnt, 0, &val); @@ -248,8 +267,15 @@ static ssize_t ufsdbg_tag_stats_write(struct file *filp, ufs_stats->enabled = true; pr_debug("%s: Enabling & Resetting UFS tag statistics", __func__); - memset(ufs_stats->tag_stats, 0, - sizeof(*ufs_stats->tag_stats) * hba->nutrs); + memset(hba->ufs_stats.tag_stats[0], 0, + sizeof(**hba->ufs_stats.tag_stats) * + TS_NUM_STATS * hba->nutrs); + + /* initialize current queue depth */ + ufs_stats->q_depth = 0; + for_each_set_bit_from(bit, &hba->outstanding_reqs, hba->nutrs) + ufs_stats->q_depth++; + pr_debug("%s: Enabled UFS tag statistics", __func__); } spin_unlock_irqrestore(hba->host->host_lock, flags); @@ -264,19 +290,29 @@ static const struct file_operations ufsdbg_tag_stats_fops = { static int ufshcd_init_tag_statistics(struct ufs_hba *hba) { + struct ufs_stats *stats = &hba->ufs_stats; int ret = 0; + int i; - hba->ufs_stats.tag_stats = kzalloc(hba->nutrs * sizeof(u64), - GFP_KERNEL); - if (!hba->ufs_stats.tag_stats) { - dev_err(hba->dev, - "%s: Unable to allocate UFS tag_stats", __func__); - ret = -ENOMEM; - goto exit; - } + stats->enabled = false; + stats->tag_stats = kzalloc(sizeof(*stats->tag_stats) * hba->nutrs, + GFP_KERNEL); + if (!hba->ufs_stats.tag_stats) + goto no_mem; - hba->ufs_stats.enabled = false; + stats->tag_stats[0] = kzalloc(sizeof(**stats->tag_stats) * + TS_NUM_STATS * hba->nutrs, GFP_KERNEL); + if (!stats->tag_stats[0]) + goto no_mem; + for (i = 1; i < hba->nutrs; i++) + stats->tag_stats[i] = &stats->tag_stats[0][i * TS_NUM_STATS]; + + goto exit; + +no_mem: + dev_err(hba->dev, "%s: Unable to allocate UFS tag_stats", __func__); + ret = -ENOMEM; exit: return ret; } diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 2ab908220a81..108976bc9ef6 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -3,7 +3,7 @@ * * This code is based on drivers/scsi/ufs/ufshcd.c * Copyright (C) 2011-2013 Samsung India Software Operations - * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2013, The Linux Foundation. All rights reserved. * * Authors: * Santosh Yaraganavi @@ -51,19 +51,47 @@ #ifdef CONFIG_DEBUG_FS #define UFSHCD_UPDATE_TAG_STATS(hba, tag) \ do { \ - if (hba->ufs_stats.enabled) { \ - hba->ufs_stats.tag_stats[tag]++; \ - } \ - } while (0); + struct request *rq = hba->lrb[task_tag].cmd ? \ + hba->lrb[task_tag].cmd->request : NULL; \ + u64 **tag_stats = hba->ufs_stats.tag_stats; \ + int rq_type = -1; \ + if (!hba->ufs_stats.enabled) \ + break; \ + tag_stats[tag][TS_TAG]++; \ + if (!rq) \ + break; \ + WARN_ON(hba->ufs_stats.q_depth > hba->nutrs); \ + if (rq_data_dir(rq) == READ) \ + rq_type = (rq->cmd_flags & REQ_URGENT) ?\ + TS_URGENT : TS_READ; \ + else if (rq_data_dir(rq) == WRITE) \ + rq_type = TS_WRITE; \ + else if (rq->cmd_flags & REQ_FLUSH) \ + rq_type = TS_FLUSH; \ + else \ + break; \ + tag_stats[hba->ufs_stats.q_depth++][rq_type]++; \ + } while (0) + +#define UFSHCD_UPDATE_TAG_STATS_COMPLETION(hba, cmd) \ + do { \ + struct request *rq = cmd ? cmd->request : NULL; \ + if (cmd->request && \ + ((rq_data_dir(rq) == READ) || \ + (rq_data_dir(rq) == WRITE) || \ + (rq->cmd_flags & REQ_FLUSH))) \ + hba->ufs_stats.q_depth--; \ + } while (0) #define UFSDBG_ADD_DEBUGFS(hba) ufsdbg_add_debugfs(hba); #define UFSDBG_REMOVE_DEBUGFS(hba) ufsdbg_remove_debugfs(hba); #else -#define UFSHCD_UPDATE_TAG_STATS(hba, tag) do {} while (0); -#define UFSDBG_ADD_DEBUGFS(hba) do {} while (0); -#define UFSDBG_REMOVE_DEBUGFS(hba) do {} while (0); +#define UFSHCD_UPDATE_TAG_STATS(hba, tag) +#define UFSHCD_UPDATE_TAG_STATS_COMPLETION(hba, cmd) +#define UFSDBG_ADD_DEBUGFS(hba) +#define UFSDBG_REMOVE_DEBUGFS(hba) #endif @@ -934,7 +962,7 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag) ufshcd_clk_scaling_start_busy(hba); __set_bit(task_tag, &hba->outstanding_reqs); ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL); - UFSHCD_UPDATE_TAG_STATS(hba, task_tag) + UFSHCD_UPDATE_TAG_STATS(hba, task_tag); } /** @@ -3467,6 +3495,7 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba) lrbp = &hba->lrb[index]; cmd = lrbp->cmd; if (cmd) { + UFSHCD_UPDATE_TAG_STATS_COMPLETION(hba, cmd); result = ufshcd_transfer_rsp_status(hba, lrbp); scsi_dma_unmap(cmd); cmd->result = result; diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index bca139f9b979..c5427a3e6d36 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -3,6 +3,7 @@ * * This code is based on drivers/scsi/ufs/ufshcd.h * Copyright (C) 2011-2013 Samsung India Software Operations + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. * * Authors: * Santosh Yaraganavi @@ -337,8 +338,9 @@ struct ufs_init_prefetch { #ifdef CONFIG_DEBUG_FS struct ufs_stats { - u64 *tag_stats; bool enabled; + u64 **tag_stats; + int q_depth; }; struct debugfs_files { @@ -351,6 +353,17 @@ struct debugfs_files { struct fault_attr fail_attr; #endif }; + +/* tag stats statistics types */ +enum ts_types { + TS_NOT_SUPPORTED = -1, + TS_TAG = 0, + TS_READ = 1, + TS_WRITE = 2, + TS_URGENT = 3, + TS_FLUSH = 4, + TS_NUM_STATS = 5, +}; #endif /**