Fix I/O counts in vmstat Currently, there are two functions to submit a bio, submit_bio and generic_make_request. They both do the same thing, except that submit_bio increments the I/O counter (visible in vmstat) and generic_make_request doesn't. The decision whether bio is counted or not is made by the code that submits the bio. This leads to some problems: * when we write to dm-raid1 target with two raid legs, I/O is counted three times (once on entry to dm-raid1 and once on each legs) * when dm-crypt target accepts large number of bios and sends them out, the machine appears deadlocked (there is no I/O activity in vmstat and processes are stuck in 'D' state). The machine is not really deadlocked, I/Os are submitted by dm-crypt to the disk driver, but they are not counted. This patch changes it so that the decision if the bio should or shouldn't be counted is made at the queue the bio is sent to. The bios are counted (regardless if the submitter uses submit_bio or generic_make_reuqest) unless the queue has a flag "QUEUE_FLAG_NO_IO_COUNT". QUEUE_FLAG_NO_IO_COUNT is sent on queues for md, dm and loop because these drivers forward the bio to some other device. Consequently, the I/O counts in vmstat are accurate, they measure the I/O throughput of physical block devices. Signed-off-by: Mikulas Patocka --- block/blk-core.c | 20 ++++++++++++++++---- drivers/block/loop.c | 2 ++ drivers/md/dm.c | 1 + drivers/md/md.c | 1 + include/linux/blkdev.h | 1 + 5 files changed, 21 insertions(+), 4 deletions(-) Index: linux-3.17-rc6/block/blk-core.c =================================================================== --- linux-3.17-rc6.orig/block/blk-core.c 2014-09-26 20:07:03.000000000 +0200 +++ linux-3.17-rc6/block/blk-core.c 2014-09-26 20:14:18.000000000 +0200 @@ -1877,6 +1877,21 @@ void generic_make_request(struct bio *bi return; /* + * If it's a regular read/write or a barrier with data attached, + * go through the normal accounting stuff before submission. + */ + if (!test_bit(QUEUE_FLAG_NO_IO_COUNT, &bdev_get_queue(bio->bi_bdev)->queue_flags) && + bio_has_data(bio) && !(bio->bi_rw & REQ_DISCARD)) { + int count = bio_sectors(bio); + if (bio->bi_rw & WRITE) { + count_vm_events(PGPGOUT, count); + } else { + task_io_account_read(bio->bi_iter.bi_size); + count_vm_events(PGPGIN, count); + } + } + + /* * We only want one ->make_request_fn to be active at a time, else * stack usage with stacked devices could be a problem. So use * current->bio_list to keep a list of requests submited by a @@ -1945,11 +1960,8 @@ void submit_bio(int rw, struct bio *bio) else count = bio_sectors(bio); - if (rw & WRITE) { - count_vm_events(PGPGOUT, count); - } else { + if (!(rw & WRITE)) { task_io_account_read(bio->bi_iter.bi_size); - count_vm_events(PGPGIN, count); } if (unlikely(block_dump)) { Index: linux-3.17-rc6/include/linux/blkdev.h =================================================================== --- linux-3.17-rc6.orig/include/linux/blkdev.h 2014-09-26 20:07:40.000000000 +0200 +++ linux-3.17-rc6/include/linux/blkdev.h 2014-09-26 20:14:04.000000000 +0200 @@ -515,6 +515,7 @@ struct request_queue { #define QUEUE_FLAG_INIT_DONE 20 /* queue is initialized */ #define QUEUE_FLAG_NO_SG_MERGE 21 /* don't attempt to merge SG segments*/ #define QUEUE_FLAG_SG_GAPS 22 /* queue doesn't support SG gaps */ +#define QUEUE_FLAG_NO_IO_COUNT 23 /* don't increase io request count */ #define QUEUE_FLAG_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \ (1 << QUEUE_FLAG_STACKABLE) | \ Index: linux-3.17-rc6/drivers/block/loop.c =================================================================== --- linux-3.17-rc6.orig/drivers/block/loop.c 2014-09-26 20:05:18.000000000 +0200 +++ linux-3.17-rc6/drivers/block/loop.c 2014-09-26 20:14:04.000000000 +0200 @@ -1637,6 +1637,8 @@ static int loop_add(struct loop_device * blk_queue_make_request(lo->lo_queue, loop_make_request); lo->lo_queue->queuedata = lo; + queue_flag_set_unlocked(QUEUE_FLAG_NO_IO_COUNT, lo->lo_queue); + disk = lo->lo_disk = alloc_disk(1 << part_shift); if (!disk) goto out_free_queue; Index: linux-3.17-rc6/drivers/md/md.c =================================================================== --- linux-3.17-rc6.orig/drivers/md/md.c 2014-09-26 20:07:14.000000000 +0200 +++ linux-3.17-rc6/drivers/md/md.c 2014-09-26 20:14:04.000000000 +0200 @@ -4843,6 +4843,7 @@ static int md_alloc(dev_t dev, char *nam blk_queue_make_request(mddev->queue, md_make_request); blk_set_stacking_limits(&mddev->queue->limits); + queue_flag_set_unlocked(QUEUE_FLAG_NO_IO_COUNT, mddev->queue); disk = alloc_disk(1 << shift); if (!disk) { Index: linux-3.17-rc6/drivers/md/dm.c =================================================================== --- linux-3.17-rc6.orig/drivers/md/dm.c 2014-09-26 20:05:18.000000000 +0200 +++ linux-3.17-rc6/drivers/md/dm.c 2014-09-26 20:14:18.000000000 +0200 @@ -1908,6 +1908,7 @@ static void dm_init_md_queue(struct mapp * This queue is new, so no concurrency on the queue_flags. */ queue_flag_clear_unlocked(QUEUE_FLAG_STACKABLE, md->queue); + queue_flag_set_unlocked(QUEUE_FLAG_NO_IO_COUNT, md->queue); md->queue->queuedata = md; md->queue->backing_dev_info.congested_fn = dm_any_congested;