Commit 7833b865 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by David Sterba

btrfs: fix iomap_begin length for nocow writes

can_nocow_extent can reduce the len passed in, which needs to be
propagated to btrfs_dio_iomap_begin so that iomap does not submit
more data then is mapped.

This problems exists since the btrfs_get_blocks_direct helper was added
in commit c5794e51 ("btrfs: Factor out write portion of
btrfs_get_blocks_direct"), but the ordered_extent splitting added in
commit b73a6fd1 ("btrfs: split partial dio bios before submit")
added a WARN_ON that made a syzkaller test fail.

Reported-by: syzbot+ee90502d5c8fd1d0dd93@syzkaller.appspotmail.com
Fixes: c5794e51 ("btrfs: Factor out write portion of btrfs_get_blocks_direct")
CC: stable@vger.kernel.org # 6.1+
Tested-by: syzbot+ee90502d5c8fd1d0dd93@syzkaller.appspotmail.com
Reviewed-by: default avatarFilipe Manana <fdmanana@suse.com>
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 79b8ee70
...@@ -7264,7 +7264,7 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start, ...@@ -7264,7 +7264,7 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start,
static int btrfs_get_blocks_direct_write(struct extent_map **map, static int btrfs_get_blocks_direct_write(struct extent_map **map,
struct inode *inode, struct inode *inode,
struct btrfs_dio_data *dio_data, struct btrfs_dio_data *dio_data,
u64 start, u64 len, u64 start, u64 *lenp,
unsigned int iomap_flags) unsigned int iomap_flags)
{ {
const bool nowait = (iomap_flags & IOMAP_NOWAIT); const bool nowait = (iomap_flags & IOMAP_NOWAIT);
...@@ -7275,6 +7275,7 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map, ...@@ -7275,6 +7275,7 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map,
struct btrfs_block_group *bg; struct btrfs_block_group *bg;
bool can_nocow = false; bool can_nocow = false;
bool space_reserved = false; bool space_reserved = false;
u64 len = *lenp;
u64 prev_len; u64 prev_len;
int ret = 0; int ret = 0;
...@@ -7345,15 +7346,19 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map, ...@@ -7345,15 +7346,19 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map,
free_extent_map(em); free_extent_map(em);
*map = NULL; *map = NULL;
if (nowait) if (nowait) {
return -EAGAIN; ret = -EAGAIN;
goto out;
}
/* /*
* If we could not allocate data space before locking the file * If we could not allocate data space before locking the file
* range and we can't do a NOCOW write, then we have to fail. * range and we can't do a NOCOW write, then we have to fail.
*/ */
if (!dio_data->data_space_reserved) if (!dio_data->data_space_reserved) {
return -ENOSPC; ret = -ENOSPC;
goto out;
}
/* /*
* We have to COW and we have already reserved data space before, * We have to COW and we have already reserved data space before,
...@@ -7394,6 +7399,7 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map, ...@@ -7394,6 +7399,7 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map,
btrfs_delalloc_release_extents(BTRFS_I(inode), len); btrfs_delalloc_release_extents(BTRFS_I(inode), len);
btrfs_delalloc_release_metadata(BTRFS_I(inode), len, true); btrfs_delalloc_release_metadata(BTRFS_I(inode), len, true);
} }
*lenp = len;
return ret; return ret;
} }
...@@ -7570,7 +7576,7 @@ static int btrfs_dio_iomap_begin(struct inode *inode, loff_t start, ...@@ -7570,7 +7576,7 @@ static int btrfs_dio_iomap_begin(struct inode *inode, loff_t start,
if (write) { if (write) {
ret = btrfs_get_blocks_direct_write(&em, inode, dio_data, ret = btrfs_get_blocks_direct_write(&em, inode, dio_data,
start, len, flags); start, &len, flags);
if (ret < 0) if (ret < 0)
goto unlock_err; goto unlock_err;
unlock_extents = true; unlock_extents = true;
......
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