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
100100
101101 Since 5.0 this field counts jiffies when at least one request was
102102 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.
104104
105105 Field 11 -- weighted # of milliseconds spent doing I/Os (unsigned int)
106106 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
143143 summed to) and the result given to the user. There is no convenient
144144 user interface for accessing the per-CPU counters themselves.
145145
146+Since 4.19 request times are measured with nanoseconds precision and
147+truncated to milliseconds before showing in this interface.
148+
146149 Disks vs Partitions
147150 -------------------
148151
block/bio.c+4−4
@@ -1768,14 +1768,14 @@ defer: · 1767 unmodified lines
17681768 schedule_work(&bio_dirty_work);
17691769 }
17701770
1771−void update_io_ticks(struct hd_struct *part, unsigned long now)
1771+void update_io_ticks(struct hd_struct *part, unsigned long now, bool end)
17721772 {
17731773 unsigned long stamp;
17741774 again:
17751775 stamp = READ_ONCE(part->stamp);
17761776 if (unlikely(stamp != now)) {
17771777 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);
17791779 }
17801780 }
17811781 if (part->partno) {
@@ -1791,7 +1791,7 @@ void generic_start_io_acct(struct request_queue *q, int op, · 9 unmodified lines
17911791
17921792 part_stat_lock();
17931793
1794− update_io_ticks(part, jiffies);
1794+ update_io_ticks(part, jiffies, false);
17951795 part_stat_inc(part, ios[sgrp]);
17961796 part_stat_add(part, sectors[sgrp], sectors);
17971797 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
18091809
18101810 part_stat_lock();
18111811
1812− update_io_ticks(part, now);
1812+ update_io_ticks(part, now, true);
18131813 part_stat_add(part, nsecs[sgrp], jiffies_to_nsecs(duration));
18141814 part_stat_add(part, time_in_queue, duration);
18151815 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
13371337 part_stat_lock();
13381338 part = req->part;
13391339
1340− update_io_ticks(part, jiffies);
1340+ update_io_ticks(part, jiffies, true);
13411341 part_stat_inc(part, ios[sgrp]);
13421342 part_stat_add(part, nsecs[sgrp], now - req->start_time_ns);
13431343 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
13791379 rq->part = part;
13801380 }
13811381
1382− update_io_ticks(part, jiffies);
1382+ update_io_ticks(part, jiffies, false);
13831383
13841384 part_stat_unlock();
13851385 }
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
422422 void part_inc_in_flight(struct request_queue *q, struct hd_struct *part,
423423 int rw);
424424
425−void update_io_ticks(struct hd_struct *part, unsigned long now);
425+void update_io_ticks(struct hd_struct *part, unsigned long now, bool end);
426426
427427 /* block/genhd.c */
428428 extern void device_add_disk(struct device *parent, struct gendisk *disk,