Skip to content

Commit 2b8bd42

Browse files
koct9iaxboe
authored andcommitted
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. Fixes: 5b18b5a ("block: delete part_round_stats and switch to less precise counting") Signed-off-by: Konstantin Khlebnikov <[email protected]> Reviewed-by: Ming Lei <[email protected]> Signed-off-by: Jens Axboe <[email protected]>
1 parent 387048b commit 2b8bd42

4 files changed

Lines changed: 11 additions & 8 deletions

File tree

Documentation/admin-guide/iostats.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ Field 10 -- # of milliseconds spent doing I/Os (unsigned int)
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
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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1768,14 +1768,14 @@ void bio_check_pages_dirty(struct bio *bio)
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,
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,
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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,7 +1337,7 @@ void blk_account_io_done(struct request *req, u64 now)
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)
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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ void part_dec_in_flight(struct request_queue *q, struct hd_struct *part,
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,

0 commit comments

Comments
 (0)