From: Artur Paszkiewicz <artur.paszkiewicz@xxxxxxxxx> Use generic io accounting functions to manage io stats. There was an attempt to do this earlier in commit 18c0b223cf990172 ("md: use generic io stats accounting functions to simplify io stat accounting"), but it did not include a call to generic_end_io_acct() and caused issues with tracking in-flight IOs, so it was later removed in commit 74672d069b29 ("md: fix md io stats accounting broken"). This patch attempts to fix this by using both generic_start_io_acct() and generic_end_io_acct(). To make it possible, in md_make_request() a bio is cloned with additional data - struct md_io, which includes the io start_time. A new bioset is introduced for this purpose. We call generic_start_io_acct() and pass the clone instead of the original to md_handle_request(). When it completes, we call generic_end_io_acct() and complete the original bio. This adds correct statistics about in-flight IOs and IO processing time, interpreted e.g. in iostat as await, svctm, aqu-sz and %util. It also fixes a situation where too many IOs where reported if a bio was re-submitted to the mddev, because io accounting is now performed only on newly arriving bios. Signed-off-by: Artur Paszkiewicz <artur.paszkiewicz@xxxxxxxxx> [Guoqing: rebase and make generic accounting applies to personalities which don't have clone infrastructure] Signed-off-by: Guoqing Jiang <jiangguoqing@xxxxxxxxxx> --- drivers/md/md.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++- drivers/md/md.h | 1 + 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/drivers/md/md.c b/drivers/md/md.c index 7ba00e4c862d..35355c187377 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -441,6 +441,25 @@ void md_handle_request(struct mddev *mddev, struct bio *bio) } EXPORT_SYMBOL(md_handle_request); +struct md_io { + struct mddev *mddev; + struct bio *orig_bio; + unsigned long start_time; + struct bio orig_bio_clone; +}; + +static void md_end_io(struct bio *bio) +{ + struct md_io *md_io = bio->bi_private; + struct bio *orig_bio = md_io->orig_bio; + + orig_bio->bi_status = bio->bi_status; + + bio_end_io_acct_remapped(orig_bio, md_io->start_time, orig_bio->bi_bdev); + bio_put(bio); + bio_endio(orig_bio); +} + static blk_qc_t md_submit_bio(struct bio *bio) { const int rw = bio_data_dir(bio); @@ -465,6 +484,29 @@ static blk_qc_t md_submit_bio(struct bio *bio) return BLK_QC_T_NONE; } + /* + * We don't clone bio for multipath, raid1 and raid10 since we can reuse + * their clone infrastructure. + */ + if (blk_queue_io_stat(bio->bi_bdev->bd_disk->queue) && + (bio->bi_pool != &mddev->md_io_bs) && + (mddev->level != 1) && (mddev->level != 10) && + (mddev->level != LEVEL_MULTIPATH)) { + struct md_io *md_io; + struct bio *clone; + + clone = bio_clone_fast(bio, GFP_NOIO, &mddev->md_io_bs); + + md_io = container_of(clone, struct md_io, orig_bio_clone); + md_io->mddev = mddev; + md_io->orig_bio = bio; + md_io->start_time = bio_start_io_acct(bio); + + clone->bi_end_io = md_end_io; + clone->bi_private = md_io; + bio = clone; + } + /* bio could be mergeable after passing to underlayer */ bio->bi_opf &= ~REQ_NOMERGE; @@ -2340,7 +2382,8 @@ int md_integrity_register(struct mddev *mddev) bdev_get_integrity(reference->bdev)); pr_debug("md: data integrity enabled on %s\n", mdname(mddev)); - if (bioset_integrity_create(&mddev->bio_set, BIO_POOL_SIZE)) { + if (bioset_integrity_create(&mddev->bio_set, BIO_POOL_SIZE) || + bioset_integrity_create(&mddev->md_io_bs, BIO_POOL_SIZE)) { pr_err("md: failed to create integrity pool for %s\n", mdname(mddev)); return -EINVAL; @@ -5569,6 +5612,7 @@ static void md_free(struct kobject *ko) bioset_exit(&mddev->bio_set); bioset_exit(&mddev->sync_set); + bioset_exit(&mddev->md_io_bs); kfree(mddev); } @@ -5864,6 +5908,12 @@ int md_run(struct mddev *mddev) if (err) return err; } + if (!bioset_initialized(&mddev->md_io_bs)) { + err = bioset_init(&mddev->md_io_bs, BIO_POOL_SIZE, + offsetof(struct md_io, orig_bio_clone), 0); + if (err) + return err; + } spin_lock(&pers_lock); pers = find_pers(mddev->level, mddev->clevel); @@ -6041,6 +6091,7 @@ int md_run(struct mddev *mddev) abort: bioset_exit(&mddev->bio_set); bioset_exit(&mddev->sync_set); + bioset_exit(&mddev->md_io_bs); return err; } EXPORT_SYMBOL_GPL(md_run); @@ -6264,6 +6315,7 @@ void md_stop(struct mddev *mddev) __md_stop(mddev); bioset_exit(&mddev->bio_set); bioset_exit(&mddev->sync_set); + bioset_exit(&mddev->md_io_bs); } EXPORT_SYMBOL_GPL(md_stop); diff --git a/drivers/md/md.h b/drivers/md/md.h index 4da240ffe2c5..1dece6b73c9e 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -487,6 +487,7 @@ struct mddev { struct bio_set sync_set; /* for sync operations like * metadata and bitmap writes */ + struct bio_set md_io_bs; /* for io accounting */ /* Generic flush handling. * The last to finish preflush schedules a worker to submit -- 2.25.1