@@ -27,6 +27,14 @@ Description:
For more details refer Documentation/admin-guide/iostats.rst
+What: /sys/block/<disk>/io_latency
+Date: January 2021
+Contact: Guoqing Jiang <guoqing.jiang@cloud.ionos.com>
+Description:
+ The /sys/block/<disk>/io_latency files displays the I/O
+ latency of disk <disk>. With it, it is convenient to know
+ the statistics of I/O latency for each type (read, write,
+ discard and flush) which have happened to the disk.
What: /sys/block/<disk>/<part>/stat
Date: February 2008
@@ -1264,6 +1264,26 @@ static void update_io_ticks(struct block_device *part, unsigned long now,
}
}
+static void blk_additional_latency(struct block_device *part, const int sgrp,
+ struct request_queue *q,
+ unsigned long duration)
+{
+ unsigned int idx;
+
+ if (!blk_queue_io_extra_stat(q))
+ return;
+
+ duration /= NSEC_PER_MSEC;
+ duration /= HZ_TO_MSEC_NUM;
+ if (likely(duration > 0)) {
+ idx = ilog2(duration);
+ if (idx > ADD_STAT_NUM - 1)
+ idx = ADD_STAT_NUM - 1;
+ } else
+ idx = 0;
+ part_stat_inc(part, latency_table[idx][sgrp]);
+}
+
static void blk_account_io_completion(struct request *req, unsigned int bytes)
{
if (req->part && blk_do_io_stat(req)) {
@@ -1288,6 +1308,8 @@ void blk_account_io_done(struct request *req, u64 now)
part_stat_lock();
update_io_ticks(req->part, jiffies, true);
+ blk_additional_latency(req->part, sgrp, req->q,
+ now - req->start_time_ns);
part_stat_inc(req->part, ios[sgrp]);
part_stat_add(req->part, nsecs[sgrp], now - req->start_time_ns);
part_stat_unlock();
@@ -1354,6 +1376,8 @@ static void __part_end_io_acct(struct block_device *part, unsigned int op,
part_stat_lock();
update_io_ticks(part, now, true);
+ blk_additional_latency(part, sgrp, part->bd_disk->queue,
+ jiffies_to_nsecs(duration));
part_stat_add(part, nsecs[sgrp], jiffies_to_nsecs(duration));
part_stat_local_dec(part, in_flight[op_is_write(op)]);
part_stat_unlock();
@@ -1146,6 +1146,42 @@ static struct device_attribute dev_attr_fail_timeout =
__ATTR(io-timeout-fail, 0644, part_timeout_show, part_timeout_store);
#endif
+static ssize_t io_latency_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct block_device *bdev = dev_to_bdev(dev);
+ size_t count = 0;
+ int i, sgrp;
+
+ for (i = 0; i < ADD_STAT_NUM; i++) {
+ unsigned int from, to;
+
+ if (i == ADD_STAT_NUM - 1) {
+ count += scnprintf(buf + count, PAGE_SIZE - count, " >= %5d ms: ",
+ (2 << (i - 2)) * HZ_TO_MSEC_NUM);
+ } else {
+ if (i < 2) {
+ from = i;
+ to = i + 1;
+ } else {
+ from = 2 << (i - 2);
+ to = 2 << (i - 1);
+ }
+ count += scnprintf(buf + count, PAGE_SIZE - count, "[%5d - %-5d) ms: ",
+ from * HZ_TO_MSEC_NUM, to * HZ_TO_MSEC_NUM);
+ }
+
+ for (sgrp = 0; sgrp < NR_STAT_GROUPS; sgrp++)
+ count += scnprintf(buf + count, PAGE_SIZE - count, "%lu ",
+ part_stat_read(bdev, latency_table[i][sgrp]));
+ count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
+ }
+
+ return count;
+}
+
+static struct device_attribute dev_attr_io_latency =
+ __ATTR(io_latency, 0444, io_latency_show, NULL);
+
static struct attribute *disk_attrs[] = {
&dev_attr_range.attr,
&dev_attr_ext_range.attr,
@@ -1165,6 +1201,7 @@ static struct attribute *disk_attrs[] = {
#ifdef CONFIG_FAIL_IO_TIMEOUT
&dev_attr_fail_timeout.attr,
#endif
+ &dev_attr_io_latency.attr,
NULL
};
@@ -9,6 +9,11 @@ struct disk_stats {
unsigned long sectors[NR_STAT_GROUPS];
unsigned long ios[NR_STAT_GROUPS];
unsigned long merges[NR_STAT_GROUPS];
+ /*
+ * We measure latency (ms) for 1, 2, ..., 1024 and >=1024.
+ */
+#define ADD_STAT_NUM 12
+ unsigned long latency_table[ADD_STAT_NUM][NR_STAT_GROUPS];
unsigned long io_ticks;
local_t in_flight[2];
};