diff options
-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, |