mmc: Add profiling code to measure performance at MMC layers.

Profiling code is added to measure read, write times for
the MMC requests at various MMC layers. Profiling is done
at the MMC queue and at the driver level. This information
can be viewed through a sysfs entry called perf.

Change-Id: I7c65bfe25a1f7774e3a9abf1f9539e690b3718ec
Signed-off-by: Aparna Mallavarapu <aparnam@qualcomm.com>
This commit is contained in:
Aparna Mallavarapu 2010-06-11 18:13:05 +05:30 committed by Stephen Boyd
parent 0b35f473c9
commit 6ccd10f3bc
4 changed files with 113 additions and 0 deletions

View file

@ -19,6 +19,14 @@ config MMC_DEBUG
This is an option for use by developers; most people should
say N here. This enables MMC core and driver debugging.
config MMC_PERF_PROFILING
bool "MMC performance profiling"
depends on MMC != n
default n
help
If you say Y here, support will be added for collecting
performance numbers at the MMC Queue and Host layers.
if MMC
source "drivers/mmc/core/Kconfig"

View file

@ -135,6 +135,9 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
{
struct mmc_command *cmd = mrq->cmd;
int err = cmd->error;
#ifdef CONFIG_MMC_PERF_PROFILING
ktime_t diff;
#endif
if (err && cmd->retries && mmc_host_is_spi(host)) {
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
@ -159,6 +162,20 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
cmd->resp[2], cmd->resp[3]);
if (mrq->data) {
#ifdef CONFIG_MMC_PERF_PROFILING
diff = ktime_sub(ktime_get(), host->perf.start);
if (mrq->data->flags == MMC_DATA_READ) {
host->perf.rbytes_drv +=
mrq->data->bytes_xfered;
host->perf.rtime_drv =
ktime_add(host->perf.rtime_drv, diff);
} else {
host->perf.wbytes_drv +=
mrq->data->bytes_xfered;
host->perf.wtime_drv =
ktime_add(host->perf.wtime_drv, diff);
}
#endif
pr_debug("%s: %d bytes transferred: %d\n",
mmc_hostname(host),
mrq->data->bytes_xfered, mrq->data->error);
@ -239,6 +256,9 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
mrq->stop->error = 0;
mrq->stop->mrq = mrq;
}
#ifdef CONFIG_MMC_PERF_PROFILING
host->perf.start = ktime_get();
#endif
}
mmc_host_clk_hold(host);
led_trigger_event(host->led, LED_FULL);

View file

@ -355,6 +355,70 @@ free:
}
EXPORT_SYMBOL(mmc_alloc_host);
#ifdef CONFIG_MMC_PERF_PROFILING
static ssize_t
show_perf(struct device *dev, struct device_attribute *attr, char *buf)
{
struct mmc_host *host = dev_get_drvdata(dev);
int64_t rtime_mmcq, wtime_mmcq, rtime_drv, wtime_drv;
unsigned long rbytes_mmcq, wbytes_mmcq, rbytes_drv, wbytes_drv;
spin_lock(&host->lock);
rbytes_mmcq = host->perf.rbytes_mmcq;
wbytes_mmcq = host->perf.wbytes_mmcq;
rbytes_drv = host->perf.rbytes_drv;
wbytes_drv = host->perf.wbytes_drv;
rtime_mmcq = ktime_to_us(host->perf.rtime_mmcq);
wtime_mmcq = ktime_to_us(host->perf.wtime_mmcq);
rtime_drv = ktime_to_us(host->perf.rtime_drv);
wtime_drv = ktime_to_us(host->perf.wtime_drv);
spin_unlock(&host->lock);
return snprintf(buf, PAGE_SIZE, "Write performance at MMCQ Level:"
"%lu bytes in %lld microseconds\n"
"Read performance at MMCQ Level:"
"%lu bytes in %lld microseconds\n"
"Write performance at driver Level:"
"%lu bytes in %lld microseconds\n"
"Read performance at driver Level:"
"%lu bytes in %lld microseconds\n",
wbytes_mmcq, wtime_mmcq, rbytes_mmcq,
rtime_mmcq, wbytes_drv, wtime_drv,
rbytes_drv, rtime_drv);
}
static ssize_t
set_perf(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int64_t value;
struct mmc_host *host = dev_get_drvdata(dev);
sscanf(buf, "%lld", &value);
if (!value) {
spin_lock(&host->lock);
memset(&host->perf, 0, sizeof(host->perf));
spin_unlock(&host->lock);
}
return count;
}
static DEVICE_ATTR(perf, S_IRUGO | S_IWUSR,
show_perf, set_perf);
#endif
static struct attribute *dev_attrs[] = {
#ifdef CONFIG_MMC_PERF_PROFILING
&dev_attr_perf.attr,
#endif
NULL,
};
static struct attribute_group dev_attr_grp = {
.attrs = dev_attrs,
};
/**
* mmc_add_host - initialise host hardware
@ -382,6 +446,11 @@ int mmc_add_host(struct mmc_host *host)
#endif
mmc_host_clk_sysfs_init(host);
err = sysfs_create_group(&host->parent->kobj, &dev_attr_grp);
if (err)
pr_err("%s: failed to create sysfs group with err %d\n",
__func__, err);
mmc_start_host(host);
if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
register_pm_notifier(&host->pm_notify);
@ -409,6 +478,8 @@ void mmc_remove_host(struct mmc_host *host)
#ifdef CONFIG_DEBUG_FS
mmc_remove_host_debugfs(host);
#endif
sysfs_remove_group(&host->parent->kobj, &dev_attr_grp);
device_del(&host->class_dev);

View file

@ -334,6 +334,20 @@ struct mmc_host {
} embedded_sdio_data;
#endif
#ifdef CONFIG_MMC_PERF_PROFILING
struct {
unsigned long rbytes_mmcq; /* Rd bytes MMC queue */
unsigned long wbytes_mmcq; /* Wr bytes MMC queue */
unsigned long rbytes_drv; /* Rd bytes MMC Host */
unsigned long wbytes_drv; /* Wr bytes MMC Host */
ktime_t rtime_mmcq; /* Rd time MMC queue */
ktime_t wtime_mmcq; /* Wr time MMC queue */
ktime_t rtime_drv; /* Rd time MMC Host */
ktime_t wtime_drv; /* Wr time MMC Host */
ktime_t start;
} perf;
#endif
unsigned long private[0] ____cacheline_aligned;
};