diff options
author | Chris Mason <chris.mason@oracle.com> | 2009-09-11 12:27:37 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2009-09-11 13:31:07 -0400 |
commit | a1ed835e1ab5795f91b198d08c43e2f56848dcf3 (patch) | |
tree | ac3b370823fa76c5be7698e3663306badbbd622d /fs/btrfs/inode.c | |
parent | 8b62b72b26bcd72082c4a69d179dd906bcc22200 (diff) |
Btrfs: Fix extent replacment race
Data COW means that whenever we write to a file, we replace any old
extent pointers with new ones. There was a window where a readpage
might find the old extent pointers on disk and cache them in the
extent_map tree in ram in the middle of a given write replacing them.
Even though both the readpage and the write had their respective bytes
in the file locked, the extent readpage inserts may cover more bytes than
it had locked down.
This commit closes the race by keeping the new extent pinned in the extent
map tree until after the on-disk btree is properly setup with the new
extent pointers.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/inode.c')
-rw-r--r-- | fs/btrfs/inode.c | 25 |
1 files changed, 20 insertions, 5 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 739a245e25d6..233fe6f26120 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -232,7 +232,8 @@ static noinline int cow_file_range_inline(struct btrfs_trans_handle *trans, | |||
232 | } | 232 | } |
233 | 233 | ||
234 | ret = btrfs_drop_extents(trans, root, inode, start, | 234 | ret = btrfs_drop_extents(trans, root, inode, start, |
235 | aligned_end, aligned_end, start, &hint_byte); | 235 | aligned_end, aligned_end, start, |
236 | &hint_byte, 1); | ||
236 | BUG_ON(ret); | 237 | BUG_ON(ret); |
237 | 238 | ||
238 | if (isize > actual_end) | 239 | if (isize > actual_end) |
@@ -241,7 +242,7 @@ static noinline int cow_file_range_inline(struct btrfs_trans_handle *trans, | |||
241 | inline_len, compressed_size, | 242 | inline_len, compressed_size, |
242 | compressed_pages); | 243 | compressed_pages); |
243 | BUG_ON(ret); | 244 | BUG_ON(ret); |
244 | btrfs_drop_extent_cache(inode, start, aligned_end, 0); | 245 | btrfs_drop_extent_cache(inode, start, aligned_end - 1, 0); |
245 | return 0; | 246 | return 0; |
246 | } | 247 | } |
247 | 248 | ||
@@ -1455,9 +1456,19 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, | |||
1455 | BUG_ON(!path); | 1456 | BUG_ON(!path); |
1456 | 1457 | ||
1457 | path->leave_spinning = 1; | 1458 | path->leave_spinning = 1; |
1459 | |||
1460 | /* | ||
1461 | * we may be replacing one extent in the tree with another. | ||
1462 | * The new extent is pinned in the extent map, and we don't want | ||
1463 | * to drop it from the cache until it is completely in the btree. | ||
1464 | * | ||
1465 | * So, tell btrfs_drop_extents to leave this extent in the cache. | ||
1466 | * the caller is expected to unpin it and allow it to be merged | ||
1467 | * with the others. | ||
1468 | */ | ||
1458 | ret = btrfs_drop_extents(trans, root, inode, file_pos, | 1469 | ret = btrfs_drop_extents(trans, root, inode, file_pos, |
1459 | file_pos + num_bytes, locked_end, | 1470 | file_pos + num_bytes, locked_end, |
1460 | file_pos, &hint); | 1471 | file_pos, &hint, 0); |
1461 | BUG_ON(ret); | 1472 | BUG_ON(ret); |
1462 | 1473 | ||
1463 | ins.objectid = inode->i_ino; | 1474 | ins.objectid = inode->i_ino; |
@@ -1485,7 +1496,6 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, | |||
1485 | btrfs_mark_buffer_dirty(leaf); | 1496 | btrfs_mark_buffer_dirty(leaf); |
1486 | 1497 | ||
1487 | inode_add_bytes(inode, num_bytes); | 1498 | inode_add_bytes(inode, num_bytes); |
1488 | btrfs_drop_extent_cache(inode, file_pos, file_pos + num_bytes - 1, 0); | ||
1489 | 1499 | ||
1490 | ins.objectid = disk_bytenr; | 1500 | ins.objectid = disk_bytenr; |
1491 | ins.offset = disk_num_bytes; | 1501 | ins.offset = disk_num_bytes; |
@@ -1596,6 +1606,9 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end) | |||
1596 | ordered_extent->len, | 1606 | ordered_extent->len, |
1597 | compressed, 0, 0, | 1607 | compressed, 0, 0, |
1598 | BTRFS_FILE_EXTENT_REG); | 1608 | BTRFS_FILE_EXTENT_REG); |
1609 | unpin_extent_cache(&BTRFS_I(inode)->extent_tree, | ||
1610 | ordered_extent->file_offset, | ||
1611 | ordered_extent->len); | ||
1599 | BUG_ON(ret); | 1612 | BUG_ON(ret); |
1600 | } | 1613 | } |
1601 | unlock_extent(io_tree, ordered_extent->file_offset, | 1614 | unlock_extent(io_tree, ordered_extent->file_offset, |
@@ -2940,7 +2953,7 @@ int btrfs_cont_expand(struct inode *inode, loff_t size) | |||
2940 | cur_offset, | 2953 | cur_offset, |
2941 | cur_offset + hole_size, | 2954 | cur_offset + hole_size, |
2942 | block_end, | 2955 | block_end, |
2943 | cur_offset, &hint_byte); | 2956 | cur_offset, &hint_byte, 1); |
2944 | if (err) | 2957 | if (err) |
2945 | break; | 2958 | break; |
2946 | err = btrfs_insert_file_extent(trans, root, | 2959 | err = btrfs_insert_file_extent(trans, root, |
@@ -5086,6 +5099,8 @@ static int prealloc_file_range(struct btrfs_trans_handle *trans, | |||
5086 | 0, 0, 0, | 5099 | 0, 0, 0, |
5087 | BTRFS_FILE_EXTENT_PREALLOC); | 5100 | BTRFS_FILE_EXTENT_PREALLOC); |
5088 | BUG_ON(ret); | 5101 | BUG_ON(ret); |
5102 | btrfs_drop_extent_cache(inode, cur_offset, | ||
5103 | cur_offset + ins.offset -1, 0); | ||
5089 | num_bytes -= ins.offset; | 5104 | num_bytes -= ins.offset; |
5090 | cur_offset += ins.offset; | 5105 | cur_offset += ins.offset; |
5091 | alloc_hint = ins.objectid + ins.offset; | 5106 | alloc_hint = ins.objectid + ins.offset; |