scsi: ufs: add queue fullness statistics

Add more statistics to allow tracking of tags occupancy
upon sending a new request. The statistics is kept separately
for 4 types of requests: read, write, urgent and flush.
All will consist only of data requests (eg. read, write, urgent).
This statistic is an enhancement of current tag statistic and
uses same infrastructure.

Change-Id: If5cea4aec4e94081d568a3661584b31665becfc6
Signed-off-by: Dolev Raviv <draviv@codeaurora.org>
[subhashj@codeaurora.org: resolved trivial merge conflicts]
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
This commit is contained in:
Dolev Raviv 2014-01-12 13:51:23 +02:00 committed by David Keitel
parent 8045d7daab
commit 0c8a317ea9
3 changed files with 113 additions and 35 deletions

View file

@ -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);
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]);
pr_debug("%s: Dispatched tag %d - %llu times\n",
__func__, i, ufs_stats->tag_stats[i]);
/* 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);
/* 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;
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),
stats->enabled = false;
stats->tag_stats = kzalloc(sizeof(*stats->tag_stats) * hba->nutrs,
GFP_KERNEL);
if (!hba->ufs_stats.tag_stats) {
dev_err(hba->dev,
"%s: Unable to allocate UFS tag_stats", __func__);
ret = -ENOMEM;
if (!hba->ufs_stats.tag_stats)
goto no_mem;
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;
}
hba->ufs_stats.enabled = false;
no_mem:
dev_err(hba->dev, "%s: Unable to allocate UFS tag_stats", __func__);
ret = -ENOMEM;
exit:
return ret;
}

View file

@ -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 <santosh.sy@samsung.com>
@ -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;

View file

@ -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 <santosh.sy@samsung.com>
@ -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
/**