ANDROID: MMC/UFS IO Latency Histograms.

This patch adds a new sysfs node (latency_hist) and reports
IO (svc time) latency histograms. Disabled by default, can be
enabled by echoing 0 into latency_hist, stats can be cleared
by writing 2 into latency_hist. This commit fixes the 32 bit
build breakage in the previous commit. Tested on both 32 bit
and 64 bit arm devices. (Merged from android4.4-common).

Bug: 30677035
Change-Id: I9403ea093f4cd54ebae08f12a11d01ef153118d0
Signed-off-by: Mohan Srinivasan <srmohan@google.com>
tirimbino
Mohan Srinivasan 8 years ago committed by Amit Pundir
parent d863018fb2
commit b2c388b758
  1. 84
      block/blk-core.c
  2. 75
      drivers/mmc/core/core.c
  3. 10
      drivers/mmc/core/host.c
  4. 5
      drivers/mmc/core/host.h
  5. 81
      drivers/scsi/ufs/ufshcd.c
  6. 3
      drivers/scsi/ufs/ufshcd.h
  7. 75
      include/linux/blkdev.h
  8. 4
      include/linux/mmc/core.h
  9. 6
      include/linux/mmc/host.h

@ -43,6 +43,8 @@
#include "blk-mq-sched.h"
#include "blk-wbt.h"
#include <linux/math64.h>
#ifdef CONFIG_DEBUG_FS
struct dentry *blk_debugfs_root;
#endif
@ -3602,3 +3604,85 @@ int __init blk_dev_init(void)
return 0;
}
/*
* Blk IO latency support. We want this to be as cheap as possible, so doing
* this lockless (and avoiding atomics), a few off by a few errors in this
* code is not harmful, and we don't want to do anything that is
* perf-impactful.
* TODO : If necessary, we can make the histograms per-cpu and aggregate
* them when printing them out.
*/
void
blk_zero_latency_hist(struct io_latency_state *s)
{
memset(s->latency_y_axis_read, 0,
sizeof(s->latency_y_axis_read));
memset(s->latency_y_axis_write, 0,
sizeof(s->latency_y_axis_write));
s->latency_reads_elems = 0;
s->latency_writes_elems = 0;
}
EXPORT_SYMBOL(blk_zero_latency_hist);
ssize_t
blk_latency_hist_show(struct io_latency_state *s, char *buf)
{
int i;
int bytes_written = 0;
u_int64_t num_elem, elem;
int pct;
num_elem = s->latency_reads_elems;
if (num_elem > 0) {
bytes_written += scnprintf(buf + bytes_written,
PAGE_SIZE - bytes_written,
"IO svc_time Read Latency Histogram (n = %llu):\n",
num_elem);
for (i = 0;
i < ARRAY_SIZE(latency_x_axis_us);
i++) {
elem = s->latency_y_axis_read[i];
pct = div64_u64(elem * 100, num_elem);
bytes_written += scnprintf(buf + bytes_written,
PAGE_SIZE - bytes_written,
"\t< %5lluus%15llu%15d%%\n",
latency_x_axis_us[i],
elem, pct);
}
/* Last element in y-axis table is overflow */
elem = s->latency_y_axis_read[i];
pct = div64_u64(elem * 100, num_elem);
bytes_written += scnprintf(buf + bytes_written,
PAGE_SIZE - bytes_written,
"\t> %5dms%15llu%15d%%\n", 10,
elem, pct);
}
num_elem = s->latency_writes_elems;
if (num_elem > 0) {
bytes_written += scnprintf(buf + bytes_written,
PAGE_SIZE - bytes_written,
"IO svc_time Write Latency Histogram (n = %llu):\n",
num_elem);
for (i = 0;
i < ARRAY_SIZE(latency_x_axis_us);
i++) {
elem = s->latency_y_axis_write[i];
pct = div64_u64(elem * 100, num_elem);
bytes_written += scnprintf(buf + bytes_written,
PAGE_SIZE - bytes_written,
"\t< %5lluus%15llu%15d%%\n",
latency_x_axis_us[i],
elem, pct);
}
/* Last element in y-axis table is overflow */
elem = s->latency_y_axis_write[i];
pct = div64_u64(elem * 100, num_elem);
bytes_written += scnprintf(buf + bytes_written,
PAGE_SIZE - bytes_written,
"\t> %5dms%15llu%15d%%\n", 10,
elem, pct);
}
return bytes_written;
}
EXPORT_SYMBOL(blk_latency_hist_show);

@ -198,6 +198,19 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
pr_debug("%s: %d bytes transferred: %d\n",
mmc_hostname(host),
mrq->data->bytes_xfered, mrq->data->error);
#ifdef CONFIG_BLOCK
if (mrq->lat_hist_enabled) {
ktime_t completion;
u_int64_t delta_us;
completion = ktime_get();
delta_us = ktime_us_delta(completion,
mrq->io_start);
blk_update_latency_hist(&host->io_lat_s,
(mrq->data->flags & MMC_DATA_READ),
delta_us);
}
#endif
}
if (mrq->stop) {
@ -633,8 +646,16 @@ struct mmc_async_req *mmc_start_areq(struct mmc_host *host,
return NULL;
/* Fine so far, start the new request! */
if (status == MMC_BLK_SUCCESS && areq)
if (status == MMC_BLK_SUCCESS && areq) {
#ifdef CONFIG_BLOCK
if (host->latency_hist_enabled) {
areq->mrq->io_start = ktime_get();
areq->mrq->lat_hist_enabled = 1;
} else
areq->mrq->lat_hist_enabled = 0;
#endif
start_err = __mmc_start_data_req(host, areq->mrq);
}
/* Postprocess the old request at this point */
if (host->areq)
@ -1853,7 +1874,7 @@ void mmc_init_erase(struct mmc_card *card)
}
static unsigned int mmc_mmc_erase_timeout(struct mmc_card *card,
unsigned int arg, unsigned int qty)
unsigned int arg, unsigned int qty)
{
unsigned int erase_timeout;
@ -2862,6 +2883,56 @@ static void __exit mmc_exit(void)
mmc_unregister_bus();
}
#ifdef CONFIG_BLOCK
static ssize_t
latency_hist_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct mmc_host *host = cls_dev_to_mmc_host(dev);
return blk_latency_hist_show(&host->io_lat_s, buf);
}
/*
* Values permitted 0, 1, 2.
* 0 -> Disable IO latency histograms (default)
* 1 -> Enable IO latency histograms
* 2 -> Zero out IO latency histograms
*/
static ssize_t
latency_hist_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct mmc_host *host = cls_dev_to_mmc_host(dev);
long value;
if (kstrtol(buf, 0, &value))
return -EINVAL;
if (value == BLK_IO_LAT_HIST_ZERO)
blk_zero_latency_hist(&host->io_lat_s);
else if (value == BLK_IO_LAT_HIST_ENABLE ||
value == BLK_IO_LAT_HIST_DISABLE)
host->latency_hist_enabled = value;
return count;
}
static DEVICE_ATTR(latency_hist, S_IRUGO | S_IWUSR,
latency_hist_show, latency_hist_store);
void
mmc_latency_hist_sysfs_init(struct mmc_host *host)
{
if (device_create_file(&host->class_dev, &dev_attr_latency_hist))
dev_err(&host->class_dev,
"Failed to create latency_hist sysfs entry\n");
}
void
mmc_latency_hist_sysfs_exit(struct mmc_host *host)
{
device_remove_file(&host->class_dev, &dev_attr_latency_hist);
}
#endif
subsys_initcall(mmc_init);
module_exit(mmc_exit);

@ -32,8 +32,6 @@
#include "pwrseq.h"
#include "sdio_ops.h"
#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev)
static DEFINE_IDA(mmc_host_ida);
static void mmc_host_classdev_release(struct device *dev)
@ -422,6 +420,10 @@ int mmc_add_host(struct mmc_host *host)
mmc_add_host_debugfs(host);
#endif
#ifdef CONFIG_BLOCK
mmc_latency_hist_sysfs_init(host);
#endif
mmc_start_host(host);
if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
mmc_register_pm_notifier(host);
@ -449,6 +451,10 @@ void mmc_remove_host(struct mmc_host *host)
mmc_remove_host_debugfs(host);
#endif
#ifdef CONFIG_BLOCK
mmc_latency_hist_sysfs_exit(host);
#endif
device_del(&host->class_dev);
led_trigger_unregister_simple(host->led);

@ -13,6 +13,8 @@
#include <linux/mmc/host.h>
#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev)
int mmc_register_host_class(void);
void mmc_unregister_host_class(void);
@ -69,5 +71,8 @@ static inline bool mmc_card_hs400es(struct mmc_card *card)
}
void mmc_latency_hist_sysfs_init(struct mmc_host *host);
void mmc_latency_hist_sysfs_exit(struct mmc_host *host);
#endif

@ -41,6 +41,7 @@
#include <linux/devfreq.h>
#include <linux/nls.h>
#include <linux/of.h>
#include <linux/blkdev.h>
#include "ufshcd.h"
#include "ufs_quirks.h"
#include "unipro.h"
@ -2340,6 +2341,17 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
clear_bit_unlock(tag, &hba->lrb_in_use);
goto out;
}
/* IO svc time latency histogram */
if (hba != NULL && cmd->request != NULL) {
if (hba->latency_hist_enabled &&
(cmd->request->cmd_type == REQ_TYPE_FS)) {
cmd->request->lat_hist_io_start = ktime_get();
cmd->request->lat_hist_enabled = 1;
} else
cmd->request->lat_hist_enabled = 0;
}
WARN_ON(hba->clk_gating.state != CLKS_ON);
lrbp = &hba->lrb[tag];
@ -4602,6 +4614,7 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
struct scsi_cmnd *cmd;
int result;
int index;
struct request *req;
for_each_set_bit(index, &completed_reqs, hba->nutrs) {
lrbp = &hba->lrb[index];
@ -4614,6 +4627,22 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
/* Mark completed command as NULL in LRB */
lrbp->cmd = NULL;
clear_bit_unlock(index, &hba->lrb_in_use);
req = cmd->request;
if (req) {
/* Update IO svc time latency histogram */
if (req->lat_hist_enabled) {
ktime_t completion;
u_int64_t delta_us;
completion = ktime_get();
delta_us = ktime_us_delta(completion,
req->lat_hist_io_start);
/* rq_data_dir() => true if WRITE */
blk_update_latency_hist(&hba->io_lat_s,
(rq_data_dir(req) == READ),
delta_us);
}
}
/* Do not touch lrbp after scsi done */
cmd->scsi_done(cmd);
__ufshcd_release(hba);
@ -7735,6 +7764,54 @@ out:
}
EXPORT_SYMBOL(ufshcd_shutdown);
/*
* Values permitted 0, 1, 2.
* 0 -> Disable IO latency histograms (default)
* 1 -> Enable IO latency histograms
* 2 -> Zero out IO latency histograms
*/
static ssize_t
latency_hist_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
long value;
if (kstrtol(buf, 0, &value))
return -EINVAL;
if (value == BLK_IO_LAT_HIST_ZERO)
blk_zero_latency_hist(&hba->io_lat_s);
else if (value == BLK_IO_LAT_HIST_ENABLE ||
value == BLK_IO_LAT_HIST_DISABLE)
hba->latency_hist_enabled = value;
return count;
}
ssize_t
latency_hist_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
return blk_latency_hist_show(&hba->io_lat_s, buf);
}
static DEVICE_ATTR(latency_hist, S_IRUGO | S_IWUSR,
latency_hist_show, latency_hist_store);
static void
ufshcd_init_latency_hist(struct ufs_hba *hba)
{
if (device_create_file(hba->dev, &dev_attr_latency_hist))
dev_err(hba->dev, "Failed to create latency_hist sysfs entry\n");
}
static void
ufshcd_exit_latency_hist(struct ufs_hba *hba)
{
device_create_file(hba->dev, &dev_attr_latency_hist);
}
/**
* ufshcd_remove - de-allocate SCSI host and host memory space
* data structure memory
@ -7751,6 +7828,7 @@ void ufshcd_remove(struct ufs_hba *hba)
ufshcd_exit_clk_gating(hba);
if (ufshcd_is_clkscaling_supported(hba))
device_remove_file(hba->dev, &hba->clk_scaling.enable_attr);
ufshcd_exit_latency_hist(hba);
ufshcd_hba_exit(hba);
}
EXPORT_SYMBOL_GPL(ufshcd_remove);
@ -7980,6 +8058,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
/* Hold auto suspend until async scan completes */
pm_runtime_get_sync(dev);
ufshcd_init_latency_hist(hba);
/*
* We are assuming that device wasn't put in sleep/power-down
* state exclusively during the boot stage before kernel.
@ -7997,6 +8077,7 @@ out_remove_scsi_host:
scsi_remove_host(hba->host);
exit_gating:
ufshcd_exit_clk_gating(hba);
ufshcd_exit_latency_hist(hba);
out_disable:
hba->is_irq_enabled = false;
ufshcd_hba_exit(hba);

@ -677,6 +677,9 @@ struct ufs_hba {
struct rw_semaphore clk_scaling_lock;
struct ufs_desc_size desc_size;
int latency_hist_enabled;
struct io_latency_state io_lat_s;
};
/* Returns true if clocks can be gated. Otherwise false */

@ -238,6 +238,9 @@ struct request {
/* for bidi */
struct request *next_rq;
ktime_t lat_hist_io_start;
int lat_hist_enabled;
};
static inline bool blk_rq_is_scsi(struct request *rq)
@ -1957,6 +1960,78 @@ extern int __blkdev_driver_ioctl(struct block_device *, fmode_t, unsigned int,
extern int bdev_read_page(struct block_device *, sector_t, struct page *);
extern int bdev_write_page(struct block_device *, sector_t, struct page *,
struct writeback_control *);
/*
* X-axis for IO latency histogram support.
*/
static const u_int64_t latency_x_axis_us[] = {
100,
200,
300,
400,
500,
600,
700,
800,
900,
1000,
1200,
1400,
1600,
1800,
2000,
2500,
3000,
4000,
5000,
6000,
7000,
9000,
10000
};
#define BLK_IO_LAT_HIST_DISABLE 0
#define BLK_IO_LAT_HIST_ENABLE 1
#define BLK_IO_LAT_HIST_ZERO 2
struct io_latency_state {
u_int64_t latency_y_axis_read[ARRAY_SIZE(latency_x_axis_us) + 1];
u_int64_t latency_reads_elems;
u_int64_t latency_y_axis_write[ARRAY_SIZE(latency_x_axis_us) + 1];
u_int64_t latency_writes_elems;
};
static inline void
blk_update_latency_hist(struct io_latency_state *s,
int read,
u_int64_t delta_us)
{
int i;
for (i = 0; i < ARRAY_SIZE(latency_x_axis_us); i++) {
if (delta_us < (u_int64_t)latency_x_axis_us[i]) {
if (read)
s->latency_y_axis_read[i]++;
else
s->latency_y_axis_write[i]++;
break;
}
}
if (i == ARRAY_SIZE(latency_x_axis_us)) {
/* Overflowed the histogram */
if (read)
s->latency_y_axis_read[i]++;
else
s->latency_y_axis_write[i]++;
}
if (read)
s->latency_reads_elems++;
else
s->latency_writes_elems++;
}
void blk_zero_latency_hist(struct io_latency_state *s);
ssize_t blk_latency_hist_show(struct io_latency_state *s, char *buf);
#else /* CONFIG_BLOCK */
struct block_device;

@ -153,6 +153,10 @@ struct mmc_request {
/* Allow other commands during this ongoing data transfer or busy wait */
bool cap_cmd_during_tfr;
ktime_t io_start;
#ifdef CONFIG_BLOCK
int lat_hist_enabled;
#endif
};
struct mmc_card;

@ -13,6 +13,7 @@
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/fault-inject.h>
#include <linux/blkdev.h>
#include <linux/mmc/core.h>
#include <linux/mmc/card.h>
@ -402,6 +403,11 @@ struct mmc_host {
} embedded_sdio_data;
#endif
#ifdef CONFIG_BLOCK
int latency_hist_enabled;
struct io_latency_state io_lat_s;
#endif
unsigned long private[0] ____cacheline_aligned;
};

Loading…
Cancel
Save