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 */ #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 */
for (i = 0 ; i < max_depth ; ++i) { seq_printf(file, " Tag Stat\t\t%s Queue Fullness\n", sep);
if (hba->ufs_stats.tag_stats[i] != 0) { for (i = 0; i < TAB_CHARS * (TS_NUM_STATS + 4); i++) {
is_tag_empty = false; seq_puts(file, "-");
seq_printf(file, if (i == (TAB_CHARS * 3 - 1))
"%s: Dispatched tag %d - %llu times\n", seq_puts(file, sep);
__func__, i, ufs_stats->tag_stats[i]);
pr_debug("%s: Dispatched tag %d - %llu times\n",
__func__, i, ufs_stats->tag_stats[i]);
} }
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); 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;
stats->tag_stats = kzalloc(sizeof(*stats->tag_stats) * hba->nutrs,
GFP_KERNEL); GFP_KERNEL);
if (!hba->ufs_stats.tag_stats) { if (!hba->ufs_stats.tag_stats)
dev_err(hba->dev, goto no_mem;
"%s: Unable to allocate UFS tag_stats", __func__);
ret = -ENOMEM; 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; goto exit;
}
hba->ufs_stats.enabled = false;
no_mem:
dev_err(hba->dev, "%s: Unable to allocate UFS tag_stats", __func__);
ret = -ENOMEM;
exit: exit:
return ret; return ret;
} }

View file

@ -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;

View file

@ -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
/** /**