aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/inode.c
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2009-09-11 12:27:37 -0400
committerChris Mason <chris.mason@oracle.com>2009-09-11 13:31:07 -0400
commita1ed835e1ab5795f91b198d08c43e2f56848dcf3 (patch)
treeac3b370823fa76c5be7698e3663306badbbd622d /fs/btrfs/inode.c
parent8b62b72b26bcd72082c4a69d179dd906bcc22200 (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.c25
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;