History

block/diskstats: more accurate approximation of io_ticks for slow disks

Currently io_ticks is approximated by adding one at each start and end of requests if jiffies counter has changed. This works perfectly for requests shorter than a jiffy or if one of requests starts/ends at each jiffy. If disk executes just one request at a time and they are longer than two jiffies then only first and last jiffies will be accounted. Fix is simple: at the end of request add up into io_ticks jiffies passed since last update rather than just one jiffy. Example: common HDD executes random read 4k requests around 12ms. fio --name=test --filename=/dev/sdb --rw=randread --direct=1 --runtime=30 & iostat -x 10 sdb Note changes of iostat's "%util" 8,43% -> 99,99% before/after patch: Before: Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util sdb 0,00 0,00 82,60 0,00 330,40 0,00 8,00 0,96 12,09 12,09 0,00 1,02 8,43 After: Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util sdb 0,00 0,00 82,50 0,00 330,00 0,00 8,00 1,00 12,10 12,10 0,00 12,12 99,99 Now io_ticks does not loose time between start and end of requests, but for queue-depth > 1 some I/O time between adjacent starts might be lost. For load estimation "%util" is not as useful as average queue length, but it clearly shows how often disk queue is completely empty.

Konstantin Khlebnikov committed 6 years ago

Commit 2b8bd421 parent 387048b4 files · +11 −8Browse files

Unified · Split

4 files
  1. Documentation/admin-guide/iostats.rst +4 −1
  2. block/bio.c +4 −4
  3. block/blk-core.c +2 −2
  4. include/linux/genhd.h +1 −1
Documentation/admin-guide/iostats.rst+4−1
@@ -100,7 +100,7 @@ Field 10 -- # of milliseconds spent doing I/Os (unsigned int) · 99 unmodified lines
100 100
101 Since 5.0 this field counts jiffies when at least one request was101 Since 5.0 this field counts jiffies when at least one request was
102 started or completed. If request runs more than 2 jiffies then some102 started or completed. If request runs more than 2 jiffies then some
103 I/O time will not be accounted unless there are other requests.103 I/O time might be not accounted in case of concurrent requests.
104 104
105Field 11 -- weighted # of milliseconds spent doing I/Os (unsigned int)105Field 11 -- weighted # of milliseconds spent doing I/Os (unsigned int)
106 This field is incremented at each I/O start, I/O completion, I/O106 This field is incremented at each I/O start, I/O completion, I/O
@@ -143,6 +143,9 @@ are summed (possibly overflowing the unsigned long variable they are · 36 unmodified lines
143summed to) and the result given to the user. There is no convenient143summed to) and the result given to the user. There is no convenient
144user interface for accessing the per-CPU counters themselves.144user interface for accessing the per-CPU counters themselves.
145 145
146Since 4.19 request times are measured with nanoseconds precision and
147truncated to milliseconds before showing in this interface.
148
146Disks vs Partitions149Disks vs Partitions
147-------------------150-------------------
148 151
block/bio.c+4−4
@@ -1768,14 +1768,14 @@ defer: · 1767 unmodified lines
1768 schedule_work(&bio_dirty_work);1768 schedule_work(&bio_dirty_work);
1769}1769}
1770 1770
1771void update_io_ticks(struct hd_struct *part, unsigned long now)1771void update_io_ticks(struct hd_struct *part, unsigned long now, bool end)
1772{1772{
1773 unsigned long stamp;1773 unsigned long stamp;
1774again:1774again:
1775 stamp = READ_ONCE(part->stamp);1775 stamp = READ_ONCE(part->stamp);
1776 if (unlikely(stamp != now)) {1776 if (unlikely(stamp != now)) {
1777 if (likely(cmpxchg(&part->stamp, stamp, now) == stamp)) {1777 if (likely(cmpxchg(&part->stamp, stamp, now) == stamp)) {
1778 __part_stat_add(part, io_ticks, 1);1778 __part_stat_add(part, io_ticks, end ? now - stamp : 1);
1779 }1779 }
1780 }1780 }
1781 if (part->partno) {1781 if (part->partno) {
@@ -1791,7 +1791,7 @@ void generic_start_io_acct(struct request_queue *q, int op, · 9 unmodified lines
1791 1791
1792 part_stat_lock();1792 part_stat_lock();
1793 1793
1794 update_io_ticks(part, jiffies);1794 update_io_ticks(part, jiffies, false);
1795 part_stat_inc(part, ios[sgrp]);1795 part_stat_inc(part, ios[sgrp]);
1796 part_stat_add(part, sectors[sgrp], sectors);1796 part_stat_add(part, sectors[sgrp], sectors);
1797 part_inc_in_flight(q, part, op_is_write(op));1797 part_inc_in_flight(q, part, op_is_write(op));
@@ -1809,7 +1809,7 @@ void generic_end_io_acct(struct request_queue *q, int req_op, · 11 unmodified lines
1809 1809
1810 part_stat_lock();1810 part_stat_lock();
1811 1811
1812 update_io_ticks(part, now);1812 update_io_ticks(part, now, true);
1813 part_stat_add(part, nsecs[sgrp], jiffies_to_nsecs(duration));1813 part_stat_add(part, nsecs[sgrp], jiffies_to_nsecs(duration));
1814 part_stat_add(part, time_in_queue, duration);1814 part_stat_add(part, time_in_queue, duration);
1815 part_dec_in_flight(q, part, op_is_write(req_op));1815 part_dec_in_flight(q, part, op_is_write(req_op));
block/blk-core.c+2−2
@@ -1337,7 +1337,7 @@ void blk_account_io_done(struct request *req, u64 now) · 1336 unmodified lines
1337 part_stat_lock();1337 part_stat_lock();
1338 part = req->part;1338 part = req->part;
1339 1339
1340 update_io_ticks(part, jiffies);1340 update_io_ticks(part, jiffies, true);
1341 part_stat_inc(part, ios[sgrp]);1341 part_stat_inc(part, ios[sgrp]);
1342 part_stat_add(part, nsecs[sgrp], now - req->start_time_ns);1342 part_stat_add(part, nsecs[sgrp], now - req->start_time_ns);
1343 part_stat_add(part, time_in_queue, nsecs_to_jiffies64(now - req->start_time_ns));1343 part_stat_add(part, time_in_queue, nsecs_to_jiffies64(now - req->start_time_ns));
@@ -1379,7 +1379,7 @@ void blk_account_io_start(struct request *rq, bool new_io) · 35 unmodified lines
1379 rq->part = part;1379 rq->part = part;
1380 }1380 }
1381 1381
1382 update_io_ticks(part, jiffies);1382 update_io_ticks(part, jiffies, false);
1383 1383
1384 part_stat_unlock();1384 part_stat_unlock();
1385}1385}
include/linux/genhd.h+1−1
@@ -422,7 +422,7 @@ void part_dec_in_flight(struct request_queue *q, struct hd_struct *part, · 421 unmodified lines
422void part_inc_in_flight(struct request_queue *q, struct hd_struct *part,422void part_inc_in_flight(struct request_queue *q, struct hd_struct *part,
423 int rw);423 int rw);
424 424
425void update_io_ticks(struct hd_struct *part, unsigned long now);425void update_io_ticks(struct hd_struct *part, unsigned long now, bool end);
426 426
427/* block/genhd.c */427/* block/genhd.c */
428extern void device_add_disk(struct device *parent, struct gendisk *disk,428extern void device_add_disk(struct device *parent, struct gendisk *disk,