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:
parent
8045d7daab
commit
0c8a317ea9
3 changed files with 113 additions and 35 deletions
|
@ -169,15 +169,17 @@ static void ufsdbg_setup_fault_injection(struct ufs_hba *hba)
|
||||||
#endif /* CONFIG_UFS_FAULT_INJECTION */
|
#endif /* CONFIG_UFS_FAULT_INJECTION */
|
||||||
|
|
||||||
#define BUFF_LINE_CAPACITY 16
|
#define BUFF_LINE_CAPACITY 16
|
||||||
|
#define TAB_CHARS 8
|
||||||
|
|
||||||
static int ufsdbg_tag_stats_show(struct seq_file *file, void *data)
|
static int ufsdbg_tag_stats_show(struct seq_file *file, void *data)
|
||||||
{
|
{
|
||||||
struct ufs_hba *hba = (struct ufs_hba *)file->private;
|
struct ufs_hba *hba = (struct ufs_hba *)file->private;
|
||||||
struct ufs_stats *ufs_stats;
|
struct ufs_stats *ufs_stats;
|
||||||
int i;
|
int i, j;
|
||||||
int max_depth;
|
int max_depth;
|
||||||
bool is_tag_empty = true;
|
bool is_tag_empty = true;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
char *sep = " | * | ";
|
||||||
|
|
||||||
if (!hba)
|
if (!hba)
|
||||||
goto exit;
|
goto exit;
|
||||||
|
@ -186,27 +188,44 @@ static int ufsdbg_tag_stats_show(struct seq_file *file, void *data)
|
||||||
|
|
||||||
if (!ufs_stats->enabled) {
|
if (!ufs_stats->enabled) {
|
||||||
pr_debug("%s: ufs statistics are disabled\n", __func__);
|
pr_debug("%s: ufs statistics are disabled\n", __func__);
|
||||||
|
seq_puts(file, "ufs statistics are disabled");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
max_depth = hba->nutrs;
|
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);
|
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) {
|
/* values */
|
||||||
if (hba->ufs_stats.tag_stats[i] != 0) {
|
for (i = 0; i < max_depth; i++) {
|
||||||
is_tag_empty = false;
|
if (ufs_stats->tag_stats[i][0] <= 0 &&
|
||||||
seq_printf(file,
|
ufs_stats->tag_stats[i][1] <= 0 &&
|
||||||
"%s: Dispatched tag %d - %llu times\n",
|
ufs_stats->tag_stats[i][2] <= 0 &&
|
||||||
__func__, i, ufs_stats->tag_stats[i]);
|
ufs_stats->tag_stats[i][3] <= 0 &&
|
||||||
|
ufs_stats->tag_stats[i][4] <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
pr_debug("%s: Dispatched tag %d - %llu times\n",
|
is_tag_empty = false;
|
||||||
__func__, i, ufs_stats->tag_stats[i]);
|
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);
|
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_hba *hba = filp->f_mapping->host->i_private;
|
||||||
struct ufs_stats *ufs_stats;
|
struct ufs_stats *ufs_stats;
|
||||||
int val = 0;
|
int val = 0;
|
||||||
int ret;
|
int ret, bit = 0;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
ret = kstrtoint_from_user(ubuf, cnt, 0, &val);
|
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;
|
ufs_stats->enabled = true;
|
||||||
pr_debug("%s: Enabling & Resetting UFS tag statistics",
|
pr_debug("%s: Enabling & Resetting UFS tag statistics",
|
||||||
__func__);
|
__func__);
|
||||||
memset(ufs_stats->tag_stats, 0,
|
memset(hba->ufs_stats.tag_stats[0], 0,
|
||||||
sizeof(*ufs_stats->tag_stats) * hba->nutrs);
|
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);
|
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)
|
static int ufshcd_init_tag_statistics(struct ufs_hba *hba)
|
||||||
{
|
{
|
||||||
|
struct ufs_stats *stats = &hba->ufs_stats;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
hba->ufs_stats.tag_stats = kzalloc(hba->nutrs * sizeof(u64),
|
stats->enabled = false;
|
||||||
GFP_KERNEL);
|
stats->tag_stats = kzalloc(sizeof(*stats->tag_stats) * hba->nutrs,
|
||||||
if (!hba->ufs_stats.tag_stats) {
|
GFP_KERNEL);
|
||||||
dev_err(hba->dev,
|
if (!hba->ufs_stats.tag_stats)
|
||||||
"%s: Unable to allocate UFS tag_stats", __func__);
|
goto no_mem;
|
||||||
ret = -ENOMEM;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
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:
|
exit:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* This code is based on drivers/scsi/ufs/ufshcd.c
|
* This code is based on drivers/scsi/ufs/ufshcd.c
|
||||||
* Copyright (C) 2011-2013 Samsung India Software Operations
|
* 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:
|
* Authors:
|
||||||
* Santosh Yaraganavi <santosh.sy@samsung.com>
|
* Santosh Yaraganavi <santosh.sy@samsung.com>
|
||||||
|
@ -51,19 +51,47 @@
|
||||||
#ifdef CONFIG_DEBUG_FS
|
#ifdef CONFIG_DEBUG_FS
|
||||||
#define UFSHCD_UPDATE_TAG_STATS(hba, tag) \
|
#define UFSHCD_UPDATE_TAG_STATS(hba, tag) \
|
||||||
do { \
|
do { \
|
||||||
if (hba->ufs_stats.enabled) { \
|
struct request *rq = hba->lrb[task_tag].cmd ? \
|
||||||
hba->ufs_stats.tag_stats[tag]++; \
|
hba->lrb[task_tag].cmd->request : NULL; \
|
||||||
} \
|
u64 **tag_stats = hba->ufs_stats.tag_stats; \
|
||||||
} while (0);
|
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_ADD_DEBUGFS(hba) ufsdbg_add_debugfs(hba);
|
||||||
|
|
||||||
#define UFSDBG_REMOVE_DEBUGFS(hba) ufsdbg_remove_debugfs(hba);
|
#define UFSDBG_REMOVE_DEBUGFS(hba) ufsdbg_remove_debugfs(hba);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define UFSHCD_UPDATE_TAG_STATS(hba, tag) do {} while (0);
|
#define UFSHCD_UPDATE_TAG_STATS(hba, tag)
|
||||||
#define UFSDBG_ADD_DEBUGFS(hba) do {} while (0);
|
#define UFSHCD_UPDATE_TAG_STATS_COMPLETION(hba, cmd)
|
||||||
#define UFSDBG_REMOVE_DEBUGFS(hba) do {} while (0);
|
#define UFSDBG_ADD_DEBUGFS(hba)
|
||||||
|
#define UFSDBG_REMOVE_DEBUGFS(hba)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -934,7 +962,7 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
|
||||||
ufshcd_clk_scaling_start_busy(hba);
|
ufshcd_clk_scaling_start_busy(hba);
|
||||||
__set_bit(task_tag, &hba->outstanding_reqs);
|
__set_bit(task_tag, &hba->outstanding_reqs);
|
||||||
ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL);
|
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];
|
lrbp = &hba->lrb[index];
|
||||||
cmd = lrbp->cmd;
|
cmd = lrbp->cmd;
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
|
UFSHCD_UPDATE_TAG_STATS_COMPLETION(hba, cmd);
|
||||||
result = ufshcd_transfer_rsp_status(hba, lrbp);
|
result = ufshcd_transfer_rsp_status(hba, lrbp);
|
||||||
scsi_dma_unmap(cmd);
|
scsi_dma_unmap(cmd);
|
||||||
cmd->result = result;
|
cmd->result = result;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*
|
*
|
||||||
* This code is based on drivers/scsi/ufs/ufshcd.h
|
* This code is based on drivers/scsi/ufs/ufshcd.h
|
||||||
* Copyright (C) 2011-2013 Samsung India Software Operations
|
* Copyright (C) 2011-2013 Samsung India Software Operations
|
||||||
|
* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
* Santosh Yaraganavi <santosh.sy@samsung.com>
|
* Santosh Yaraganavi <santosh.sy@samsung.com>
|
||||||
|
@ -337,8 +338,9 @@ struct ufs_init_prefetch {
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_FS
|
#ifdef CONFIG_DEBUG_FS
|
||||||
struct ufs_stats {
|
struct ufs_stats {
|
||||||
u64 *tag_stats;
|
|
||||||
bool enabled;
|
bool enabled;
|
||||||
|
u64 **tag_stats;
|
||||||
|
int q_depth;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct debugfs_files {
|
struct debugfs_files {
|
||||||
|
@ -351,6 +353,17 @@ struct debugfs_files {
|
||||||
struct fault_attr fail_attr;
|
struct fault_attr fail_attr;
|
||||||
#endif
|
#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
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Reference in a new issue