Btrfs: use bio_clone_bioset_partial to simplify DIO submit
authorLiu Bo <bo.li.liu@oracle.com>
Tue, 16 May 2017 16:51:39 +0000 (09:51 -0700)
committerDavid Sterba <dsterba@suse.com>
Mon, 19 Jun 2017 16:25:58 +0000 (18:25 +0200)
Currently when mapping bio to limit bio to a single stripe length, we
split bio by adding page to bio one by one, but later we don't modify
the vector of bio at all, thus we can use bio_clone_fast to use the
original bio vector directly.

Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/inode.c

index c61881df2338a6863765f5aa0b43e0377224d4e5..c2e9c51c650e7d939dc387d37366db361a38b6a4 100644 (file)
@@ -8380,16 +8380,6 @@ out:
        bio_put(bio);
 }
 
-static struct bio *btrfs_dio_bio_alloc(struct block_device *bdev,
-                                      u64 first_sector, gfp_t gfp_flags)
-{
-       struct bio *bio;
-       bio = btrfs_bio_alloc(bdev, first_sector, BIO_MAX_PAGES, gfp_flags);
-       if (bio)
-               bio_associate_current(bio);
-       return bio;
-}
-
 static inline int btrfs_lookup_and_bind_dio_csum(struct inode *inode,
                                                 struct btrfs_dio_private *dip,
                                                 struct bio *bio,
@@ -8479,24 +8469,23 @@ static int btrfs_submit_direct_hook(struct btrfs_dio_private *dip,
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct bio *bio;
        struct bio *orig_bio = dip->orig_bio;
-       struct bio_vec *bvec;
        u64 start_sector = orig_bio->bi_iter.bi_sector;
        u64 file_offset = dip->logical_offset;
-       u64 submit_len = 0;
        u64 map_length;
-       u32 blocksize = fs_info->sectorsize;
        int async_submit = 0;
-       int nr_sectors;
+       u64 submit_len;
+       int clone_offset = 0;
+       int clone_len;
        int ret;
-       int i, j;
 
        map_length = orig_bio->bi_iter.bi_size;
+       submit_len = map_length;
        ret = btrfs_map_block(fs_info, btrfs_op(orig_bio), start_sector << 9,
                              &map_length, NULL, 0);
        if (ret)
                return -EIO;
 
-       if (map_length >= orig_bio->bi_iter.bi_size) {
+       if (map_length >= submit_len) {
                bio = orig_bio;
                dip->flags |= BTRFS_DIO_ORIG_BIO_SUBMITTED;
                goto submit;
@@ -8508,70 +8497,52 @@ static int btrfs_submit_direct_hook(struct btrfs_dio_private *dip,
        else
                async_submit = 1;
 
-       bio = btrfs_dio_bio_alloc(orig_bio->bi_bdev, start_sector, GFP_NOFS);
-       if (!bio)
-               return -ENOMEM;
-
-       bio->bi_opf = orig_bio->bi_opf;
-       bio->bi_private = dip;
-       bio->bi_end_io = btrfs_end_dio_bio;
-       btrfs_io_bio(bio)->logical = file_offset;
+       /* bio split */
+       ASSERT(map_length <= INT_MAX);
        atomic_inc(&dip->pending_bios);
+       while (submit_len > 0) {
+               clone_len = min_t(int, submit_len, map_length);
 
-       bio_for_each_segment_all(bvec, orig_bio, j) {
-               nr_sectors = BTRFS_BYTES_TO_BLKS(fs_info, bvec->bv_len);
-               i = 0;
-next_block:
-               if (unlikely(map_length < submit_len + blocksize ||
-                   bio_add_page(bio, bvec->bv_page, blocksize,
-                           bvec->bv_offset + (i * blocksize)) < blocksize)) {
-                       /*
-                        * inc the count before we submit the bio so
-                        * we know the end IO handler won't happen before
-                        * we inc the count. Otherwise, the dip might get freed
-                        * before we're done setting it up
-                        */
-                       atomic_inc(&dip->pending_bios);
-                       ret = __btrfs_submit_dio_bio(bio, inode,
-                                                    file_offset, skip_sum,
-                                                    async_submit);
-                       if (ret) {
-                               bio_put(bio);
-                               atomic_dec(&dip->pending_bios);
-                               goto out_err;
-                       }
-
-                       start_sector += submit_len >> 9;
-                       file_offset += submit_len;
+               /*
+                * This will never fail as it's passing GPF_NOFS and
+                * the allocation is backed by btrfs_bioset.
+                */
+               bio = btrfs_bio_clone_partial(orig_bio, GFP_NOFS, clone_offset,
+                                             clone_len);
+               bio->bi_private = dip;
+               bio->bi_end_io = btrfs_end_dio_bio;
+               btrfs_io_bio(bio)->logical = file_offset;
+
+               ASSERT(submit_len >= clone_len);
+               submit_len -= clone_len;
+               if (submit_len == 0)
+                       break;
 
-                       submit_len = 0;
+               /*
+                * Increase the count before we submit the bio so we know
+                * the end IO handler won't happen before we increase the
+                * count. Otherwise, the dip might get freed before we're
+                * done setting it up.
+                */
+               atomic_inc(&dip->pending_bios);
 
-                       bio = btrfs_dio_bio_alloc(orig_bio->bi_bdev,
-                                                 start_sector, GFP_NOFS);
-                       if (!bio)
-                               goto out_err;
-                       bio->bi_opf = orig_bio->bi_opf;
-                       bio->bi_private = dip;
-                       bio->bi_end_io = btrfs_end_dio_bio;
-                       btrfs_io_bio(bio)->logical = file_offset;
+               ret = __btrfs_submit_dio_bio(bio, inode, file_offset, skip_sum,
+                                            async_submit);
+               if (ret) {
+                       bio_put(bio);
+                       atomic_dec(&dip->pending_bios);
+                       goto out_err;
+               }
 
-                       map_length = orig_bio->bi_iter.bi_size;
-                       ret = btrfs_map_block(fs_info, btrfs_op(orig_bio),
-                                             start_sector << 9,
-                                             &map_length, NULL, 0);
-                       if (ret) {
-                               bio_put(bio);
-                               goto out_err;
-                       }
+               clone_offset += clone_len;
+               start_sector += clone_len >> 9;
+               file_offset += clone_len;
 
-                       goto next_block;
-               } else {
-                       submit_len += blocksize;
-                       if (--nr_sectors) {
-                               i++;
-                               goto next_block;
-                       }
-               }
+               map_length = submit_len;
+               ret = btrfs_map_block(fs_info, btrfs_op(orig_bio),
+                                     start_sector << 9, &map_length, NULL, 0);
+               if (ret)
+                       goto out_err;
        }
 
 submit: