mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-11-01 17:08:10 +00:00
Btrfs: fix preallocate vs double nocow write
We can not release the reserved metadata space for the first write if we find the write position is pre-allocated. Because the kernel might write the data on the disk before we do the second write but after the can-nocow check, if we release the space for the first write, we might fail to update the metadata because of no space. Fix this problem by end nocow write if there is dirty data in the range whose space is pre-allocated. Signed-off-by: Miao Xie <miaox@cn.fujitsu.com> Signed-off-by: Josef Bacik <jbacik@fb.com>
This commit is contained in:
parent
c933956ddf
commit
7b2b70851f
2 changed files with 17 additions and 8 deletions
|
@ -1427,16 +1427,11 @@ static noinline int check_can_nocow(struct inode *inode, loff_t pos,
|
||||||
|
|
||||||
num_bytes = lockend - lockstart + 1;
|
num_bytes = lockend - lockstart + 1;
|
||||||
ret = can_nocow_extent(inode, lockstart, &num_bytes, NULL, NULL, NULL);
|
ret = can_nocow_extent(inode, lockstart, &num_bytes, NULL, NULL, NULL);
|
||||||
if (ret <= 0) {
|
if (ret <= 0)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
} else {
|
else
|
||||||
clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, lockend,
|
|
||||||
EXTENT_DIRTY | EXTENT_DELALLOC |
|
|
||||||
EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, 0, 0,
|
|
||||||
NULL, GFP_NOFS);
|
|
||||||
*write_bytes = min_t(size_t, *write_bytes ,
|
*write_bytes = min_t(size_t, *write_bytes ,
|
||||||
num_bytes - pos + lockstart);
|
num_bytes - pos + lockstart);
|
||||||
}
|
|
||||||
|
|
||||||
unlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend);
|
unlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend);
|
||||||
|
|
||||||
|
|
|
@ -6557,6 +6557,7 @@ noinline int can_nocow_extent(struct inode *inode, u64 offset, u64 *len,
|
||||||
int ret;
|
int ret;
|
||||||
struct extent_buffer *leaf;
|
struct extent_buffer *leaf;
|
||||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||||
|
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
|
||||||
struct btrfs_file_extent_item *fi;
|
struct btrfs_file_extent_item *fi;
|
||||||
struct btrfs_key key;
|
struct btrfs_key key;
|
||||||
u64 disk_bytenr;
|
u64 disk_bytenr;
|
||||||
|
@ -6633,6 +6634,20 @@ noinline int can_nocow_extent(struct inode *inode, u64 offset, u64 *len,
|
||||||
|
|
||||||
if (btrfs_extent_readonly(root, disk_bytenr))
|
if (btrfs_extent_readonly(root, disk_bytenr))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
num_bytes = min(offset + *len, extent_end) - offset;
|
||||||
|
if (!nocow && found_type == BTRFS_FILE_EXTENT_PREALLOC) {
|
||||||
|
u64 range_end;
|
||||||
|
|
||||||
|
range_end = round_up(offset + num_bytes, root->sectorsize) - 1;
|
||||||
|
ret = test_range_bit(io_tree, offset, range_end,
|
||||||
|
EXTENT_DELALLOC, 0, NULL);
|
||||||
|
if (ret) {
|
||||||
|
ret = -EAGAIN;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
btrfs_release_path(path);
|
btrfs_release_path(path);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -6661,7 +6676,6 @@ noinline int can_nocow_extent(struct inode *inode, u64 offset, u64 *len,
|
||||||
*/
|
*/
|
||||||
disk_bytenr += backref_offset;
|
disk_bytenr += backref_offset;
|
||||||
disk_bytenr += offset - key.offset;
|
disk_bytenr += offset - key.offset;
|
||||||
num_bytes = min(offset + *len, extent_end) - offset;
|
|
||||||
if (csum_exist_in_range(root, disk_bytenr, num_bytes))
|
if (csum_exist_in_range(root, disk_bytenr, num_bytes))
|
||||||
goto out;
|
goto out;
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in a new issue