Commit 42034313 authored by Michal Rostecki's avatar Michal Rostecki Committed by David Sterba

btrfs: let callers of btrfs_get_io_geometry pass the em

Before this change, the btrfs_get_io_geometry() function was calling
btrfs_get_chunk_map() to get the extent mapping, necessary for
calculating the I/O geometry. It was using that extent mapping only
internally and freeing the pointer after its execution.

That resulted in calling btrfs_get_chunk_map() de facto twice by the
__btrfs_map_block() function. It was calling btrfs_get_io_geometry()
first and then calling btrfs_get_chunk_map() directly to get the extent
mapping, used by the rest of the function.

Change that to passing the extent mapping to the btrfs_get_io_geometry()
function as an argument.

This could improve performance in some cases.  For very large
filesystems, i.e. several thousands of allocated chunks, not only this
avoids searching two times the rbtree, saving time, it may also help
reducing contention on the lock that protects the tree - thinking of
writeback starting for multiple inodes, other tasks allocating or
removing chunks, and anything else that requires access to the rbtree.
Reviewed-by: default avatarFilipe Manana <fdmanana@suse.com>
Signed-off-by: default avatarMichal Rostecki <mrostecki@suse.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
[ add Filipe's analysis ]
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 951c80f8
...@@ -2183,9 +2183,10 @@ int btrfs_bio_fits_in_stripe(struct page *page, size_t size, struct bio *bio, ...@@ -2183,9 +2183,10 @@ int btrfs_bio_fits_in_stripe(struct page *page, size_t size, struct bio *bio,
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
u64 logical = bio->bi_iter.bi_sector << 9; u64 logical = bio->bi_iter.bi_sector << 9;
struct extent_map *em;
u64 length = 0; u64 length = 0;
u64 map_length; u64 map_length;
int ret; int ret = 0;
struct btrfs_io_geometry geom; struct btrfs_io_geometry geom;
if (bio_flags & EXTENT_BIO_COMPRESSED) if (bio_flags & EXTENT_BIO_COMPRESSED)
...@@ -2193,14 +2194,19 @@ int btrfs_bio_fits_in_stripe(struct page *page, size_t size, struct bio *bio, ...@@ -2193,14 +2194,19 @@ int btrfs_bio_fits_in_stripe(struct page *page, size_t size, struct bio *bio,
length = bio->bi_iter.bi_size; length = bio->bi_iter.bi_size;
map_length = length; map_length = length;
ret = btrfs_get_io_geometry(fs_info, btrfs_op(bio), logical, map_length, em = btrfs_get_chunk_map(fs_info, logical, map_length);
&geom); if (IS_ERR(em))
return PTR_ERR(em);
ret = btrfs_get_io_geometry(fs_info, em, btrfs_op(bio), logical,
map_length, &geom);
if (ret < 0) if (ret < 0)
return ret; goto out;
if (geom.len < length + size) if (geom.len < length + size)
return 1; ret = 1;
return 0; out:
free_extent_map(em);
return ret;
} }
/* /*
...@@ -7938,10 +7944,12 @@ static blk_qc_t btrfs_submit_direct(struct inode *inode, struct iomap *iomap, ...@@ -7938,10 +7944,12 @@ static blk_qc_t btrfs_submit_direct(struct inode *inode, struct iomap *iomap,
u64 submit_len; u64 submit_len;
int clone_offset = 0; int clone_offset = 0;
int clone_len; int clone_len;
u64 logical;
int ret; int ret;
blk_status_t status; blk_status_t status;
struct btrfs_io_geometry geom; struct btrfs_io_geometry geom;
struct btrfs_dio_data *dio_data = iomap->private; struct btrfs_dio_data *dio_data = iomap->private;
struct extent_map *em = NULL;
dip = btrfs_create_dio_private(dio_bio, inode, file_offset); dip = btrfs_create_dio_private(dio_bio, inode, file_offset);
if (!dip) { if (!dip) {
...@@ -7970,12 +7978,18 @@ static blk_qc_t btrfs_submit_direct(struct inode *inode, struct iomap *iomap, ...@@ -7970,12 +7978,18 @@ static blk_qc_t btrfs_submit_direct(struct inode *inode, struct iomap *iomap,
submit_len = dio_bio->bi_iter.bi_size; submit_len = dio_bio->bi_iter.bi_size;
do { do {
ret = btrfs_get_io_geometry(fs_info, btrfs_op(dio_bio), logical = start_sector << 9;
start_sector << 9, submit_len, em = btrfs_get_chunk_map(fs_info, logical, submit_len);
&geom); if (IS_ERR(em)) {
status = errno_to_blk_status(PTR_ERR(em));
em = NULL;
goto out_err_em;
}
ret = btrfs_get_io_geometry(fs_info, em, btrfs_op(dio_bio),
logical, submit_len, &geom);
if (ret) { if (ret) {
status = errno_to_blk_status(ret); status = errno_to_blk_status(ret);
goto out_err; goto out_err_em;
} }
ASSERT(geom.len <= INT_MAX); ASSERT(geom.len <= INT_MAX);
...@@ -8020,19 +8034,24 @@ static blk_qc_t btrfs_submit_direct(struct inode *inode, struct iomap *iomap, ...@@ -8020,19 +8034,24 @@ static blk_qc_t btrfs_submit_direct(struct inode *inode, struct iomap *iomap,
bio_put(bio); bio_put(bio);
if (submit_len > 0) if (submit_len > 0)
refcount_dec(&dip->refs); refcount_dec(&dip->refs);
goto out_err; goto out_err_em;
} }
dio_data->submitted += clone_len; dio_data->submitted += clone_len;
clone_offset += clone_len; clone_offset += clone_len;
start_sector += clone_len >> 9; start_sector += clone_len >> 9;
file_offset += clone_len; file_offset += clone_len;
free_extent_map(em);
} while (submit_len > 0); } while (submit_len > 0);
return BLK_QC_T_NONE; return BLK_QC_T_NONE;
out_err_em:
free_extent_map(em);
out_err: out_err:
dip->dio_bio->bi_status = status; dip->dio_bio->bi_status = status;
btrfs_dio_private_put(dip); btrfs_dio_private_put(dip);
return BLK_QC_T_NONE; return BLK_QC_T_NONE;
} }
......
...@@ -5940,23 +5940,24 @@ static bool need_full_stripe(enum btrfs_map_op op) ...@@ -5940,23 +5940,24 @@ static bool need_full_stripe(enum btrfs_map_op op)
} }
/* /*
* btrfs_get_io_geometry - calculates the geomery of a particular (address, len) * Calculate the geometry of a particular (address, len) tuple. This
* tuple. This information is used to calculate how big a * information is used to calculate how big a particular bio can get before it
* particular bio can get before it straddles a stripe. * straddles a stripe.
* *
* @fs_info - the filesystem * @fs_info: the filesystem
* @logical - address that we want to figure out the geometry of * @em: mapping containing the logical extent
* @len - the length of IO we are going to perform, starting at @logical * @op: type of operation - write or read
* @op - type of operation - write or read * @logical: address that we want to figure out the geometry of
* @io_geom - pointer used to return values * @len: the length of IO we are going to perform, starting at @logical
* @io_geom: pointer used to return values
* *
* Returns < 0 in case a chunk for the given logical address cannot be found, * Returns < 0 in case a chunk for the given logical address cannot be found,
* usually shouldn't happen unless @logical is corrupted, 0 otherwise. * usually shouldn't happen unless @logical is corrupted, 0 otherwise.
*/ */
int btrfs_get_io_geometry(struct btrfs_fs_info *fs_info, enum btrfs_map_op op, int btrfs_get_io_geometry(struct btrfs_fs_info *fs_info, struct extent_map *em,
u64 logical, u64 len, struct btrfs_io_geometry *io_geom) enum btrfs_map_op op, u64 logical, u64 len,
struct btrfs_io_geometry *io_geom)
{ {
struct extent_map *em;
struct map_lookup *map; struct map_lookup *map;
u64 offset; u64 offset;
u64 stripe_offset; u64 stripe_offset;
...@@ -5964,14 +5965,9 @@ int btrfs_get_io_geometry(struct btrfs_fs_info *fs_info, enum btrfs_map_op op, ...@@ -5964,14 +5965,9 @@ int btrfs_get_io_geometry(struct btrfs_fs_info *fs_info, enum btrfs_map_op op,
u64 stripe_len; u64 stripe_len;
u64 raid56_full_stripe_start = (u64)-1; u64 raid56_full_stripe_start = (u64)-1;
int data_stripes; int data_stripes;
int ret = 0;
ASSERT(op != BTRFS_MAP_DISCARD); ASSERT(op != BTRFS_MAP_DISCARD);
em = btrfs_get_chunk_map(fs_info, logical, len);
if (IS_ERR(em))
return PTR_ERR(em);
map = em->map_lookup; map = em->map_lookup;
/* Offset of this logical address in the chunk */ /* Offset of this logical address in the chunk */
offset = logical - em->start; offset = logical - em->start;
...@@ -5985,8 +5981,7 @@ int btrfs_get_io_geometry(struct btrfs_fs_info *fs_info, enum btrfs_map_op op, ...@@ -5985,8 +5981,7 @@ int btrfs_get_io_geometry(struct btrfs_fs_info *fs_info, enum btrfs_map_op op,
btrfs_crit(fs_info, btrfs_crit(fs_info,
"stripe math has gone wrong, stripe_offset=%llu offset=%llu start=%llu logical=%llu stripe_len=%llu", "stripe math has gone wrong, stripe_offset=%llu offset=%llu start=%llu logical=%llu stripe_len=%llu",
stripe_offset, offset, em->start, logical, stripe_len); stripe_offset, offset, em->start, logical, stripe_len);
ret = -EINVAL; return -EINVAL;
goto out;
} }
/* stripe_offset is the offset of this block in its stripe */ /* stripe_offset is the offset of this block in its stripe */
...@@ -6033,10 +6028,7 @@ int btrfs_get_io_geometry(struct btrfs_fs_info *fs_info, enum btrfs_map_op op, ...@@ -6033,10 +6028,7 @@ int btrfs_get_io_geometry(struct btrfs_fs_info *fs_info, enum btrfs_map_op op,
io_geom->stripe_offset = stripe_offset; io_geom->stripe_offset = stripe_offset;
io_geom->raid56_stripe_offset = raid56_full_stripe_start; io_geom->raid56_stripe_offset = raid56_full_stripe_start;
out: return 0;
/* once for us */
free_extent_map(em);
return ret;
} }
static int __btrfs_map_block(struct btrfs_fs_info *fs_info, static int __btrfs_map_block(struct btrfs_fs_info *fs_info,
...@@ -6069,12 +6061,13 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info, ...@@ -6069,12 +6061,13 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info,
ASSERT(bbio_ret); ASSERT(bbio_ret);
ASSERT(op != BTRFS_MAP_DISCARD); ASSERT(op != BTRFS_MAP_DISCARD);
ret = btrfs_get_io_geometry(fs_info, op, logical, *length, &geom); em = btrfs_get_chunk_map(fs_info, logical, *length);
ASSERT(!IS_ERR(em));
ret = btrfs_get_io_geometry(fs_info, em, op, logical, *length, &geom);
if (ret < 0) if (ret < 0)
return ret; return ret;
em = btrfs_get_chunk_map(fs_info, logical, *length);
ASSERT(!IS_ERR(em));
map = em->map_lookup; map = em->map_lookup;
*length = geom.len; *length = geom.len;
......
...@@ -440,8 +440,9 @@ int btrfs_map_block(struct btrfs_fs_info *fs_info, enum btrfs_map_op op, ...@@ -440,8 +440,9 @@ int btrfs_map_block(struct btrfs_fs_info *fs_info, enum btrfs_map_op op,
int btrfs_map_sblock(struct btrfs_fs_info *fs_info, enum btrfs_map_op op, int btrfs_map_sblock(struct btrfs_fs_info *fs_info, enum btrfs_map_op op,
u64 logical, u64 *length, u64 logical, u64 *length,
struct btrfs_bio **bbio_ret); struct btrfs_bio **bbio_ret);
int btrfs_get_io_geometry(struct btrfs_fs_info *fs_info, enum btrfs_map_op op, int btrfs_get_io_geometry(struct btrfs_fs_info *fs_info, struct extent_map *map,
u64 logical, u64 len, struct btrfs_io_geometry *io_geom); enum btrfs_map_op op, u64 logical, u64 len,
struct btrfs_io_geometry *io_geom);
int btrfs_read_sys_array(struct btrfs_fs_info *fs_info); int btrfs_read_sys_array(struct btrfs_fs_info *fs_info);
int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info); int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info);
int btrfs_alloc_chunk(struct btrfs_trans_handle *trans, u64 type); int btrfs_alloc_chunk(struct btrfs_trans_handle *trans, u64 type);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment