Zoned device has its own hardware restrictions e.g. max_zone_append_size
when using REQ_OP_ZONE_APPEND. To follow the restrictions, use
bio_add_zone_append_page() instead of bio_add_page(). We need target device
to use bio_add_zone_append_page(), so this commit reads the chunk
information to memoize the target device to btrfs_io_bio(bio)->device.

Currently, zoned btrfs only supports SINGLE profile. In the feature,
btrfs_io_bio can hold extent_map and check the restrictions for all the
devices the bio will be mapped.

Reviewed-by: Josef Bacik <jo...@toxicpanda.com>
Signed-off-by: Naohiro Aota <naohiro.a...@wdc.com>
---
 fs/btrfs/extent_io.c | 30 +++++++++++++++++++++++++++---
 1 file changed, 27 insertions(+), 3 deletions(-)

diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index df434f9ba774..ad19757d685d 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -3083,6 +3083,7 @@ static bool btrfs_bio_add_page(struct bio *bio, struct 
page *page,
 {
        sector_t sector = disk_bytenr >> SECTOR_SHIFT;
        bool contig;
+       int ret;
 
        if (prev_bio_flags != bio_flags)
                return false;
@@ -3097,7 +3098,12 @@ static bool btrfs_bio_add_page(struct bio *bio, struct 
page *page,
        if (btrfs_bio_fits_in_stripe(page, size, bio, bio_flags))
                return false;
 
-       return bio_add_page(bio, page, size, pg_offset) == size;
+       if (bio_op(bio) == REQ_OP_ZONE_APPEND)
+               ret = bio_add_zone_append_page(bio, page, size, pg_offset);
+       else
+               ret = bio_add_page(bio, page, size, pg_offset);
+
+       return ret == size;
 }
 
 /*
@@ -3128,7 +3134,9 @@ static int submit_extent_page(unsigned int opf,
        int ret = 0;
        struct bio *bio;
        size_t io_size = min_t(size_t, size, PAGE_SIZE);
-       struct extent_io_tree *tree = &BTRFS_I(page->mapping->host)->io_tree;
+       struct btrfs_inode *inode = BTRFS_I(page->mapping->host);
+       struct extent_io_tree *tree = &inode->io_tree;
+       struct btrfs_fs_info *fs_info = inode->root->fs_info;
 
        ASSERT(bio_ret);
 
@@ -3159,11 +3167,27 @@ static int submit_extent_page(unsigned int opf,
        if (wbc) {
                struct block_device *bdev;
 
-               bdev = 
BTRFS_I(page->mapping->host)->root->fs_info->fs_devices->latest_bdev;
+               bdev = fs_info->fs_devices->latest_bdev;
                bio_set_dev(bio, bdev);
                wbc_init_bio(wbc, bio);
                wbc_account_cgroup_owner(wbc, page, io_size);
        }
+       if (btrfs_is_zoned(fs_info) &&
+           bio_op(bio) == REQ_OP_ZONE_APPEND) {
+               struct extent_map *em;
+               struct map_lookup *map;
+
+               em = btrfs_get_chunk_map(fs_info, disk_bytenr, io_size);
+               if (IS_ERR(em))
+                       return PTR_ERR(em);
+
+               map = em->map_lookup;
+               /* We only support SINGLE profile for now */
+               ASSERT(map->num_stripes == 1);
+               btrfs_io_bio(bio)->device = map->stripes[0].dev;
+
+               free_extent_map(em);
+       }
 
        *bio_ret = bio;
 
-- 
2.27.0

Reply via email to