diff options
author | Christoph Hellwig <hch@lst.de> | 2022-08-06 11:03:30 +0300 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2022-09-26 13:27:59 +0300 |
commit | 928ff3beb8940f2a23d489d56b3cb12cf209d6db (patch) | |
tree | 35560ccecfe351dc4b142ee0975e399d3e4b6fcd /fs/btrfs/volumes.c | |
parent | 03793cbbc80fe616498f8141f853575b2d140222 (diff) | |
download | linux-928ff3beb8940f2a23d489d56b3cb12cf209d6db.tar.xz |
btrfs: stop allocation a btrfs_io_context for simple I/O
The I/O context structure is only used to pass the btrfs_device to
the end I/O handler for I/Os that go to a single device.
Stop allocating the I/O context for these cases by passing the optional
btrfs_io_stripe argument to __btrfs_map_block to query the mapping
information and then using a fast path submission and I/O completion
handler. As the old btrfs_io_context based I/O submission path is
only used for mirrored writes, rename the functions to make that
clear and stop setting the btrfs_bio device and mirror_num field
that is only used for reads.
Reviewed-by: Nikolay Borisov <nborisov@suse.com>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Anand Jain <anand.jain@oracle.com>
Tested-by: Nikolay Borisov <nborisov@suse.com>
Tested-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'fs/btrfs/volumes.c')
-rw-r--r-- | fs/btrfs/volumes.c | 94 |
1 files changed, 57 insertions, 37 deletions
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 58c999311fd6..1153a850038c 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -6706,11 +6706,12 @@ static void btrfs_log_dev_io_error(struct bio *bio, struct btrfs_device *dev) btrfs_dev_stat_inc_and_print(dev, BTRFS_DEV_STAT_FLUSH_ERRS); } -static struct workqueue_struct *btrfs_end_io_wq(struct btrfs_io_context *bioc) +static struct workqueue_struct *btrfs_end_io_wq(struct btrfs_fs_info *fs_info, + struct bio *bio) { - if (bioc->orig_bio->bi_opf & REQ_META) - return bioc->fs_info->endio_meta_workers; - return bioc->fs_info->endio_workers; + if (bio->bi_opf & REQ_META) + return fs_info->endio_meta_workers; + return fs_info->endio_workers; } static void btrfs_end_bio_work(struct work_struct *work) @@ -6721,6 +6722,24 @@ static void btrfs_end_bio_work(struct work_struct *work) bbio->end_io(bbio); } +static void btrfs_simple_end_io(struct bio *bio) +{ + struct btrfs_fs_info *fs_info = bio->bi_private; + struct btrfs_bio *bbio = btrfs_bio(bio); + + btrfs_bio_counter_dec(fs_info); + + if (bio->bi_status) + btrfs_log_dev_io_error(bio, bbio->device); + + if (bio_op(bio) == REQ_OP_READ) { + INIT_WORK(&bbio->end_io_work, btrfs_end_bio_work); + queue_work(btrfs_end_io_wq(fs_info, bio), &bbio->end_io_work); + } else { + bbio->end_io(bbio); + } +} + static void btrfs_raid56_end_io(struct bio *bio) { struct btrfs_io_context *bioc = bio->bi_private; @@ -6733,7 +6752,7 @@ static void btrfs_raid56_end_io(struct bio *bio) btrfs_put_bioc(bioc); } -static void btrfs_end_bio(struct bio *bio) +static void btrfs_orig_write_end_io(struct bio *bio) { struct btrfs_io_stripe *stripe = bio->bi_private; struct btrfs_io_context *bioc = stripe->bioc; @@ -6746,8 +6765,6 @@ static void btrfs_end_bio(struct bio *bio) btrfs_log_dev_io_error(bio, stripe->dev); } - bbio->mirror_num = bioc->mirror_num; - /* * Only send an error to the higher layers if it is beyond the tolerance * threshold. @@ -6757,13 +6774,7 @@ static void btrfs_end_bio(struct bio *bio) else bio->bi_status = BLK_STS_OK; - if (btrfs_op(bio) == BTRFS_MAP_READ) { - INIT_WORK(&bbio->end_io_work, btrfs_end_bio_work); - queue_work(btrfs_end_io_wq(bioc), &bbio->end_io_work); - } else { - bbio->end_io(bbio); - } - + bbio->end_io(bbio); btrfs_put_bioc(bioc); } @@ -6820,15 +6831,16 @@ static void btrfs_submit_dev_bio(struct btrfs_device *dev, struct bio *bio) submit_bio(bio); } -static void submit_stripe_bio(struct btrfs_io_context *bioc, int dev_nr) +static void btrfs_submit_mirrored_bio(struct btrfs_io_context *bioc, int dev_nr) { struct bio *orig_bio = bioc->orig_bio, *bio; + ASSERT(bio_op(orig_bio) != REQ_OP_READ); + /* Reuse the bio embedded into the btrfs_bio for the last mirror */ if (dev_nr == bioc->num_stripes - 1) { bio = orig_bio; - btrfs_bio(bio)->device = bioc->stripes[dev_nr].dev; - bio->bi_end_io = btrfs_end_bio; + bio->bi_end_io = btrfs_orig_write_end_io; } else { bio = bio_alloc_clone(NULL, orig_bio, GFP_NOFS, &fs_bio_set); bio_inc_remaining(orig_bio); @@ -6846,34 +6858,19 @@ void btrfs_submit_bio(struct btrfs_fs_info *fs_info, struct bio *bio, int mirror u64 logical = bio->bi_iter.bi_sector << 9; u64 length = bio->bi_iter.bi_size; u64 map_length = length; - int ret; - int dev_nr; - int total_devs; struct btrfs_io_context *bioc = NULL; + struct btrfs_io_stripe smap; + int ret; btrfs_bio_counter_inc_blocked(fs_info); ret = __btrfs_map_block(fs_info, btrfs_op(bio), logical, &map_length, - &bioc, NULL, &mirror_num, 1); + &bioc, &smap, &mirror_num, 1); if (ret) { btrfs_bio_counter_dec(fs_info); btrfs_bio_end_io(btrfs_bio(bio), errno_to_blk_status(ret)); return; } - total_devs = bioc->num_stripes; - bioc->orig_bio = bio; - - if ((bioc->map_type & BTRFS_BLOCK_GROUP_RAID56_MASK) && - ((btrfs_op(bio) == BTRFS_MAP_WRITE) || (mirror_num > 1))) { - bio->bi_private = bioc; - bio->bi_end_io = btrfs_raid56_end_io; - if (btrfs_op(bio) == BTRFS_MAP_WRITE) - raid56_parity_write(bio, bioc); - else - raid56_parity_recover(bio, bioc, mirror_num); - return; - } - if (map_length < length) { btrfs_crit(fs_info, "mapping failed logical %llu bio len %llu len %llu", @@ -6881,8 +6878,31 @@ void btrfs_submit_bio(struct btrfs_fs_info *fs_info, struct bio *bio, int mirror BUG(); } - for (dev_nr = 0; dev_nr < total_devs; dev_nr++) - submit_stripe_bio(bioc, dev_nr); + if (!bioc) { + /* Single mirror read/write fast path */ + btrfs_bio(bio)->mirror_num = mirror_num; + btrfs_bio(bio)->device = smap.dev; + bio->bi_iter.bi_sector = smap.physical >> SECTOR_SHIFT; + bio->bi_private = fs_info; + bio->bi_end_io = btrfs_simple_end_io; + btrfs_submit_dev_bio(smap.dev, bio); + } else if (bioc->map_type & BTRFS_BLOCK_GROUP_RAID56_MASK) { + /* Parity RAID write or read recovery */ + bio->bi_private = bioc; + bio->bi_end_io = btrfs_raid56_end_io; + if (bio_op(bio) == REQ_OP_READ) + raid56_parity_recover(bio, bioc, mirror_num); + else + raid56_parity_write(bio, bioc); + } else { + /* Write to multiple mirrors */ + int total_devs = bioc->num_stripes; + int dev_nr; + + bioc->orig_bio = bio; + for (dev_nr = 0; dev_nr < total_devs; dev_nr++) + btrfs_submit_mirrored_bio(bioc, dev_nr); + } } static bool dev_args_match_fs_devices(const struct btrfs_dev_lookup_args *args, |