mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 02:50:49 +09:00
btrfs: fix iomap_begin length for nocow writes
commit7833b86595upstream. 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 commitc5794e5178("btrfs: Factor out write portion of btrfs_get_blocks_direct"), but the ordered_extent splitting added in commitb73a6fd1b1("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:c5794e5178("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: Filipe Manana <fdmanana@suse.com> Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
4389fb6b6a
commit
4e9da0cda1
@@ -7387,7 +7387,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);
|
||||||
@@ -7398,6 +7398,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;
|
||||||
|
|
||||||
@@ -7468,15 +7469,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,
|
||||||
@@ -7517,6 +7522,7 @@ out:
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7693,7 +7699,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;
|
||||||
|
|||||||
Reference in New Issue
Block a user