summaryrefslogtreecommitdiffstats
path: root/fs/btrfs/file.c
diff options
context:
space:
mode:
authorYan Zheng2008-10-30 19:19:50 +0100
committerChris Mason2008-10-30 19:19:50 +0100
commit6643558db29006825dbb10012b3f8890aca4bcd5 (patch)
tree0c0f4f7a0011749cda998431828cb9161747b51a /fs/btrfs/file.c
parentBtrfs: update hole handling v2 (diff)
downloadkernel-qcow2-linux-6643558db29006825dbb10012b3f8890aca4bcd5.tar.gz
kernel-qcow2-linux-6643558db29006825dbb10012b3f8890aca4bcd5.tar.xz
kernel-qcow2-linux-6643558db29006825dbb10012b3f8890aca4bcd5.zip
Btrfs: Fix bookend extent race v2
When dropping middle part of an extent, btrfs_drop_extents truncates the extent at first, then inserts a bookend extent. Since truncation and insertion can't be done atomically, there is a small period that the bookend extent isn't in the tree. This causes problem for functions that search the tree for file extent item. The way to fix this is lock the range of the bookend extent before truncation. Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Diffstat (limited to 'fs/btrfs/file.c')
-rw-r--r--fs/btrfs/file.c31
1 files changed, 25 insertions, 6 deletions
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index b8a7637e14a1..1a0510ad030c 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -364,6 +364,7 @@ int noinline btrfs_drop_extents(struct btrfs_trans_handle *trans,
u64 start, u64 end, u64 inline_limit, u64 *hint_byte)
{
u64 extent_end = 0;
+ u64 locked_end = end;
u64 search_start = start;
u64 leaf_start;
u64 ram_bytes = 0;
@@ -479,12 +480,6 @@ next_slot:
goto next_slot;
}
- if (found_inline) {
- u64 mask = root->sectorsize - 1;
- search_start = (extent_end + mask) & ~mask;
- } else
- search_start = extent_end;
-
if (end <= extent_end && start >= key.offset && found_inline)
*hint_byte = EXTENT_MAP_INLINE;
@@ -501,6 +496,26 @@ next_slot:
if (found_inline && start <= key.offset)
keep = 1;
}
+
+ if (bookend && found_extent && locked_end < extent_end) {
+ ret = try_lock_extent(&BTRFS_I(inode)->io_tree,
+ locked_end, extent_end - 1, GFP_NOFS);
+ if (!ret) {
+ btrfs_release_path(root, path);
+ lock_extent(&BTRFS_I(inode)->io_tree,
+ locked_end, extent_end - 1, GFP_NOFS);
+ locked_end = extent_end;
+ continue;
+ }
+ locked_end = extent_end;
+ }
+
+ if (found_inline) {
+ u64 mask = root->sectorsize - 1;
+ search_start = (extent_end + mask) & ~mask;
+ } else
+ search_start = extent_end;
+
/* truncate existing extent */
if (start > key.offset) {
u64 new_num;
@@ -638,6 +653,10 @@ next_slot:
}
out:
btrfs_free_path(path);
+ if (locked_end > end) {
+ unlock_extent(&BTRFS_I(inode)->io_tree, end, locked_end - 1,
+ GFP_NOFS);
+ }
btrfs_check_file(root, inode);
return ret;
}