Btrfs: fix preallocate vs double nocow write
authorMiao Xie <miaox@cn.fujitsu.com>
Thu, 27 Feb 2014 05:58:05 +0000 (13:58 +0800)
committerJosef Bacik <jbacik@fb.com>
Mon, 10 Mar 2014 19:17:00 +0000 (15:17 -0400)
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>
fs/btrfs/file.c
fs/btrfs/inode.c

index fc2d21b0a02259e1e41270a26b409f563cd1a79d..d88f2dc4d045d757e39befe387f8d9a07095ad83 100644 (file)
@@ -1427,16 +1427,11 @@ static noinline int check_can_nocow(struct inode *inode, loff_t pos,
 
        num_bytes = lockend - lockstart + 1;
        ret = can_nocow_extent(inode, lockstart, &num_bytes, NULL, NULL, NULL);
-       if (ret <= 0) {
+       if (ret <= 0)
                ret = 0;
-       } 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);
+       else
                *write_bytes = min_t(size_t, *write_bytes ,
                                     num_bytes - pos + lockstart);
-       }
 
        unlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend);
 
index 8dba152883d300fa1c0d6092c9700757aa1df4aa..0182f081d499ff3abfc29f5db5993f5d795330ba 100644 (file)
@@ -6557,6 +6557,7 @@ noinline int can_nocow_extent(struct inode *inode, u64 offset, u64 *len,
        int ret;
        struct extent_buffer *leaf;
        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_key key;
        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))
                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);
 
        /*
@@ -6661,7 +6676,6 @@ noinline int can_nocow_extent(struct inode *inode, u64 offset, u64 *len,
         */
        disk_bytenr += backref_offset;
        disk_bytenr += offset - key.offset;
-       num_bytes = min(offset + *len, extent_end) - offset;
        if (csum_exist_in_range(root, disk_bytenr, num_bytes))
                                goto out;
        /*