blkdev: Refactoring block io latency histogram codes
The current io_latency_state structure includes entries for read and write requests. There are special types of write commands such as sync and discard (trim) commands, and the current implementation is not general enough if we want to separate latency histogram for such special commands. This change makes io_latency_state structure request-type neutral. It also changes to print the latency average. Signed-off-by: Hyojun Kim <hyojun@google.com>
This commit is contained in:
+29
-62
@@ -3583,76 +3583,43 @@ int __init blk_dev_init(void)
|
|||||||
* TODO : If necessary, we can make the histograms per-cpu and aggregate
|
* TODO : If necessary, we can make the histograms per-cpu and aggregate
|
||||||
* them when printing them out.
|
* 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
|
ssize_t
|
||||||
blk_latency_hist_show(struct io_latency_state *s, char *buf)
|
blk_latency_hist_show(char* name, struct io_latency_state *s, char *buf,
|
||||||
|
int buf_size)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int bytes_written = 0;
|
int bytes_written = 0;
|
||||||
u_int64_t num_elem, elem;
|
u_int64_t num_elem, elem;
|
||||||
int pct;
|
int pct;
|
||||||
|
u_int64_t average;
|
||||||
|
|
||||||
num_elem = s->latency_reads_elems;
|
num_elem = s->latency_elems;
|
||||||
if (num_elem > 0) {
|
if (num_elem > 0) {
|
||||||
bytes_written += scnprintf(buf + bytes_written,
|
average = div64_u64(s->latency_sum, s->latency_elems);
|
||||||
PAGE_SIZE - bytes_written,
|
bytes_written += scnprintf(buf + bytes_written,
|
||||||
"IO svc_time Read Latency Histogram (n = %llu):\n",
|
buf_size - bytes_written,
|
||||||
num_elem);
|
"IO svc_time %s Latency Histogram (n = %llu,"
|
||||||
for (i = 0;
|
" average = %llu):\n", name, num_elem, average);
|
||||||
i < ARRAY_SIZE(latency_x_axis_us);
|
for (i = 0;
|
||||||
i++) {
|
i < ARRAY_SIZE(latency_x_axis_us);
|
||||||
elem = s->latency_y_axis_read[i];
|
i++) {
|
||||||
pct = div64_u64(elem * 100, num_elem);
|
elem = s->latency_y_axis[i];
|
||||||
bytes_written += scnprintf(buf + bytes_written,
|
pct = div64_u64(elem * 100, num_elem);
|
||||||
PAGE_SIZE - bytes_written,
|
bytes_written += scnprintf(buf + bytes_written,
|
||||||
"\t< %5lluus%15llu%15d%%\n",
|
PAGE_SIZE - bytes_written,
|
||||||
latency_x_axis_us[i],
|
"\t< %6lluus%15llu%15d%%\n",
|
||||||
elem, pct);
|
latency_x_axis_us[i],
|
||||||
}
|
elem, pct);
|
||||||
/* Last element in y-axis table is overflow */
|
}
|
||||||
elem = s->latency_y_axis_read[i];
|
/* Last element in y-axis table is overflow */
|
||||||
pct = div64_u64(elem * 100, num_elem);
|
elem = s->latency_y_axis[i];
|
||||||
bytes_written += scnprintf(buf + bytes_written,
|
pct = div64_u64(elem * 100, num_elem);
|
||||||
PAGE_SIZE - bytes_written,
|
bytes_written += scnprintf(buf + bytes_written,
|
||||||
"\t> %5dms%15llu%15d%%\n", 10,
|
PAGE_SIZE - bytes_written,
|
||||||
elem, pct);
|
"\t>=%6lluus%15llu%15d%%\n",
|
||||||
}
|
latency_x_axis_us[i - 1], 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;
|
return bytes_written;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(blk_latency_hist_show);
|
EXPORT_SYMBOL(blk_latency_hist_show);
|
||||||
|
|||||||
+15
-7
@@ -209,9 +209,10 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
|
|||||||
completion = ktime_get();
|
completion = ktime_get();
|
||||||
delta_us = ktime_us_delta(completion,
|
delta_us = ktime_us_delta(completion,
|
||||||
mrq->io_start);
|
mrq->io_start);
|
||||||
blk_update_latency_hist(&host->io_lat_s,
|
blk_update_latency_hist(
|
||||||
(mrq->data->flags & MMC_DATA_READ),
|
(mrq->data->flags & MMC_DATA_READ) ?
|
||||||
delta_us);
|
&host->io_lat_read :
|
||||||
|
&host->io_lat_write, delta_us);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -3100,8 +3101,14 @@ static ssize_t
|
|||||||
latency_hist_show(struct device *dev, struct device_attribute *attr, char *buf)
|
latency_hist_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||||
|
size_t written_bytes;
|
||||||
|
|
||||||
return blk_latency_hist_show(&host->io_lat_s, buf);
|
written_bytes = blk_latency_hist_show("Read", &host->io_lat_read,
|
||||||
|
buf, PAGE_SIZE);
|
||||||
|
written_bytes += blk_latency_hist_show("Write", &host->io_lat_write,
|
||||||
|
buf + written_bytes, PAGE_SIZE - written_bytes);
|
||||||
|
|
||||||
|
return written_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -3119,9 +3126,10 @@ latency_hist_store(struct device *dev, struct device_attribute *attr,
|
|||||||
|
|
||||||
if (kstrtol(buf, 0, &value))
|
if (kstrtol(buf, 0, &value))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (value == BLK_IO_LAT_HIST_ZERO)
|
if (value == BLK_IO_LAT_HIST_ZERO) {
|
||||||
blk_zero_latency_hist(&host->io_lat_s);
|
memset(&host->io_lat_read, 0, sizeof(host->io_lat_read));
|
||||||
else if (value == BLK_IO_LAT_HIST_ENABLE ||
|
memset(&host->io_lat_write, 0, sizeof(host->io_lat_write));
|
||||||
|
} else if (value == BLK_IO_LAT_HIST_ENABLE ||
|
||||||
value == BLK_IO_LAT_HIST_DISABLE)
|
value == BLK_IO_LAT_HIST_DISABLE)
|
||||||
host->latency_hist_enabled = value;
|
host->latency_hist_enabled = value;
|
||||||
return count;
|
return count;
|
||||||
|
|||||||
@@ -3622,10 +3622,10 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
|
|||||||
completion = ktime_get();
|
completion = ktime_get();
|
||||||
delta_us = ktime_us_delta(completion,
|
delta_us = ktime_us_delta(completion,
|
||||||
req->lat_hist_io_start);
|
req->lat_hist_io_start);
|
||||||
/* rq_data_dir() => true if WRITE */
|
blk_update_latency_hist(
|
||||||
blk_update_latency_hist(&hba->io_lat_s,
|
(rq_data_dir(req) == READ) ?
|
||||||
(rq_data_dir(req) == READ),
|
&hba->io_lat_read :
|
||||||
delta_us);
|
&hba->io_lat_write, delta_us);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Do not touch lrbp after scsi done */
|
/* Do not touch lrbp after scsi done */
|
||||||
@@ -6371,9 +6371,10 @@ latency_hist_store(struct device *dev, struct device_attribute *attr,
|
|||||||
|
|
||||||
if (kstrtol(buf, 0, &value))
|
if (kstrtol(buf, 0, &value))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (value == BLK_IO_LAT_HIST_ZERO)
|
if (value == BLK_IO_LAT_HIST_ZERO) {
|
||||||
blk_zero_latency_hist(&hba->io_lat_s);
|
memset(&hba->io_lat_read, 0, sizeof(hba->io_lat_read));
|
||||||
else if (value == BLK_IO_LAT_HIST_ENABLE ||
|
memset(&hba->io_lat_write, 0, sizeof(hba->io_lat_write));
|
||||||
|
} else if (value == BLK_IO_LAT_HIST_ENABLE ||
|
||||||
value == BLK_IO_LAT_HIST_DISABLE)
|
value == BLK_IO_LAT_HIST_DISABLE)
|
||||||
hba->latency_hist_enabled = value;
|
hba->latency_hist_enabled = value;
|
||||||
return count;
|
return count;
|
||||||
@@ -6384,8 +6385,14 @@ latency_hist_show(struct device *dev, struct device_attribute *attr,
|
|||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct ufs_hba *hba = dev_get_drvdata(dev);
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
||||||
|
size_t written_bytes;
|
||||||
|
|
||||||
return blk_latency_hist_show(&hba->io_lat_s, buf);
|
written_bytes = blk_latency_hist_show("Read", &hba->io_lat_read,
|
||||||
|
buf, PAGE_SIZE);
|
||||||
|
written_bytes += blk_latency_hist_show("Write", &hba->io_lat_write,
|
||||||
|
buf + written_bytes, PAGE_SIZE - written_bytes);
|
||||||
|
|
||||||
|
return written_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEVICE_ATTR(latency_hist, S_IRUGO | S_IWUSR,
|
static DEVICE_ATTR(latency_hist, S_IRUGO | S_IWUSR,
|
||||||
|
|||||||
@@ -564,7 +564,8 @@ struct ufs_hba {
|
|||||||
enum bkops_status urgent_bkops_lvl;
|
enum bkops_status urgent_bkops_lvl;
|
||||||
bool is_urgent_bkops_lvl_checked;
|
bool is_urgent_bkops_lvl_checked;
|
||||||
int latency_hist_enabled;
|
int latency_hist_enabled;
|
||||||
struct io_latency_state io_lat_s;
|
struct io_latency_state io_lat_read;
|
||||||
|
struct io_latency_state io_lat_write;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Returns true if clocks can be gated. Otherwise false */
|
/* Returns true if clocks can be gated. Otherwise false */
|
||||||
|
|||||||
+11
-28
@@ -1738,43 +1738,26 @@ static const u_int64_t latency_x_axis_us[] = {
|
|||||||
#define BLK_IO_LAT_HIST_ZERO 2
|
#define BLK_IO_LAT_HIST_ZERO 2
|
||||||
|
|
||||||
struct io_latency_state {
|
struct io_latency_state {
|
||||||
u_int64_t latency_y_axis_read[ARRAY_SIZE(latency_x_axis_us) + 1];
|
u_int64_t latency_y_axis[ARRAY_SIZE(latency_x_axis_us) + 1];
|
||||||
u_int64_t latency_reads_elems;
|
u_int64_t latency_elems;
|
||||||
u_int64_t latency_y_axis_write[ARRAY_SIZE(latency_x_axis_us) + 1];
|
u_int64_t latency_sum;
|
||||||
u_int64_t latency_writes_elems;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
blk_update_latency_hist(struct io_latency_state *s,
|
blk_update_latency_hist(struct io_latency_state *s, u_int64_t delta_us)
|
||||||
int read,
|
|
||||||
u_int64_t delta_us)
|
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(latency_x_axis_us); i++) {
|
for (i = 0; i < ARRAY_SIZE(latency_x_axis_us); i++)
|
||||||
if (delta_us < (u_int64_t)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;
|
break;
|
||||||
}
|
s->latency_y_axis[i]++;
|
||||||
}
|
s->latency_elems++;
|
||||||
if (i == ARRAY_SIZE(latency_x_axis_us)) {
|
s->latency_sum += delta_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(char* name, struct io_latency_state *s,
|
||||||
ssize_t blk_latency_hist_show(struct io_latency_state *s, char *buf);
|
char *buf, int buf_size);
|
||||||
|
|
||||||
#else /* CONFIG_BLOCK */
|
#else /* CONFIG_BLOCK */
|
||||||
|
|
||||||
|
|||||||
@@ -409,7 +409,8 @@ struct mmc_host {
|
|||||||
|
|
||||||
#ifdef CONFIG_BLOCK
|
#ifdef CONFIG_BLOCK
|
||||||
int latency_hist_enabled;
|
int latency_hist_enabled;
|
||||||
struct io_latency_state io_lat_s;
|
struct io_latency_state io_lat_read;
|
||||||
|
struct io_latency_state io_lat_write;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
unsigned long private[0] ____cacheline_aligned;
|
unsigned long private[0] ____cacheline_aligned;
|
||||||
|
|||||||
Reference in New Issue
Block a user