diff options
author | Chris Mason <chris.mason@oracle.com> | 2008-07-22 11:18:09 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-09-25 11:04:05 -0400 |
commit | f421950f86bf96a11fef932e167ab2e70d4c43a0 (patch) | |
tree | a2b62b942b023e37b6aae39891c2b314d8d8a3fb /fs/btrfs/inode.c | |
parent | a61e6f29dc7c9d56a776a518eed92bbc61848263 (diff) |
Btrfs: Fix some data=ordered related data corruptions
Stress testing was showing data checksum errors, most of which were caused
by a lookup bug in the extent_map tree. The tree was caching the last
pointer returned, and searches would check the last pointer first.
But, search callers also expect the search to return the very first
matching extent in the range, which wasn't always true with the last
pointer usage.
For now, the code to cache the last return value is just removed. It is
easy to fix, but I think lookups are rare enough that it isn't required anymore.
This commit also replaces do_sync_mapping_range with a local copy of the
related functions.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/inode.c')
-rw-r--r-- | fs/btrfs/inode.c | 54 |
1 files changed, 30 insertions, 24 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 60852ada658e..3da12a4d913d 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -485,7 +485,7 @@ int btrfs_writepage_start_hook(struct page *page, u64 start, u64 end) | |||
485 | fixup = kzalloc(sizeof(*fixup), GFP_NOFS); | 485 | fixup = kzalloc(sizeof(*fixup), GFP_NOFS); |
486 | if (!fixup) | 486 | if (!fixup) |
487 | return -EAGAIN; | 487 | return -EAGAIN; |
488 | printk("queueing worker to fixup page %lu %Lu\n", inode->i_ino, page_offset(page)); | 488 | |
489 | SetPageChecked(page); | 489 | SetPageChecked(page); |
490 | page_cache_get(page); | 490 | page_cache_get(page); |
491 | fixup->work.func = btrfs_writepage_fixup_worker; | 491 | fixup->work.func = btrfs_writepage_fixup_worker; |
@@ -502,11 +502,13 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end) | |||
502 | struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; | 502 | struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; |
503 | struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree; | 503 | struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree; |
504 | struct extent_map *em; | 504 | struct extent_map *em; |
505 | struct extent_map *em_orig; | ||
505 | u64 alloc_hint = 0; | 506 | u64 alloc_hint = 0; |
506 | u64 clear_start; | 507 | u64 clear_start; |
507 | u64 clear_end; | 508 | u64 clear_end; |
508 | struct list_head list; | 509 | struct list_head list; |
509 | struct btrfs_key ins; | 510 | struct btrfs_key ins; |
511 | struct rb_node *rb; | ||
510 | int ret; | 512 | int ret; |
511 | 513 | ||
512 | ret = btrfs_dec_test_ordered_pending(inode, start, end - start + 1); | 514 | ret = btrfs_dec_test_ordered_pending(inode, start, end - start + 1); |
@@ -535,6 +537,22 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end) | |||
535 | 537 | ||
536 | mutex_lock(&BTRFS_I(inode)->extent_mutex); | 538 | mutex_lock(&BTRFS_I(inode)->extent_mutex); |
537 | 539 | ||
540 | spin_lock(&em_tree->lock); | ||
541 | clear_start = ordered_extent->file_offset; | ||
542 | clear_end = ordered_extent->file_offset + ordered_extent->len; | ||
543 | em = lookup_extent_mapping(em_tree, clear_start, | ||
544 | ordered_extent->len); | ||
545 | em_orig = em; | ||
546 | while(em && clear_start < extent_map_end(em) && clear_end > em->start) { | ||
547 | clear_bit(EXTENT_FLAG_PINNED, &em->flags); | ||
548 | rb = rb_next(&em->rb_node); | ||
549 | if (!rb) | ||
550 | break; | ||
551 | em = rb_entry(rb, struct extent_map, rb_node); | ||
552 | } | ||
553 | free_extent_map(em_orig); | ||
554 | spin_unlock(&em_tree->lock); | ||
555 | |||
538 | ret = btrfs_drop_extents(trans, root, inode, | 556 | ret = btrfs_drop_extents(trans, root, inode, |
539 | ordered_extent->file_offset, | 557 | ordered_extent->file_offset, |
540 | ordered_extent->file_offset + | 558 | ordered_extent->file_offset + |
@@ -548,22 +566,6 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end) | |||
548 | ordered_extent->len, 0); | 566 | ordered_extent->len, 0); |
549 | BUG_ON(ret); | 567 | BUG_ON(ret); |
550 | 568 | ||
551 | spin_lock(&em_tree->lock); | ||
552 | clear_start = ordered_extent->file_offset; | ||
553 | clear_end = ordered_extent->file_offset + ordered_extent->len; | ||
554 | while(clear_start < clear_end) { | ||
555 | em = lookup_extent_mapping(em_tree, clear_start, | ||
556 | clear_end - clear_start); | ||
557 | if (em) { | ||
558 | clear_bit(EXTENT_FLAG_PINNED, &em->flags); | ||
559 | clear_start = em->start + em->len; | ||
560 | free_extent_map(em); | ||
561 | } else { | ||
562 | break; | ||
563 | } | ||
564 | } | ||
565 | spin_unlock(&em_tree->lock); | ||
566 | |||
567 | btrfs_drop_extent_cache(inode, ordered_extent->file_offset, | 569 | btrfs_drop_extent_cache(inode, ordered_extent->file_offset, |
568 | ordered_extent->file_offset + | 570 | ordered_extent->file_offset + |
569 | ordered_extent->len - 1); | 571 | ordered_extent->len - 1); |
@@ -2318,7 +2320,7 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page, | |||
2318 | u64 extent_end = 0; | 2320 | u64 extent_end = 0; |
2319 | u64 objectid = inode->i_ino; | 2321 | u64 objectid = inode->i_ino; |
2320 | u32 found_type; | 2322 | u32 found_type; |
2321 | struct btrfs_path *path; | 2323 | struct btrfs_path *path = NULL; |
2322 | struct btrfs_root *root = BTRFS_I(inode)->root; | 2324 | struct btrfs_root *root = BTRFS_I(inode)->root; |
2323 | struct btrfs_file_extent_item *item; | 2325 | struct btrfs_file_extent_item *item; |
2324 | struct extent_buffer *leaf; | 2326 | struct extent_buffer *leaf; |
@@ -2328,9 +2330,6 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page, | |||
2328 | struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; | 2330 | struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; |
2329 | struct btrfs_trans_handle *trans = NULL; | 2331 | struct btrfs_trans_handle *trans = NULL; |
2330 | 2332 | ||
2331 | path = btrfs_alloc_path(); | ||
2332 | BUG_ON(!path); | ||
2333 | |||
2334 | again: | 2333 | again: |
2335 | spin_lock(&em_tree->lock); | 2334 | spin_lock(&em_tree->lock); |
2336 | em = lookup_extent_mapping(em_tree, start, len); | 2335 | em = lookup_extent_mapping(em_tree, start, len); |
@@ -2354,6 +2353,12 @@ again: | |||
2354 | em->bdev = root->fs_info->fs_devices->latest_bdev; | 2353 | em->bdev = root->fs_info->fs_devices->latest_bdev; |
2355 | em->start = EXTENT_MAP_HOLE; | 2354 | em->start = EXTENT_MAP_HOLE; |
2356 | em->len = (u64)-1; | 2355 | em->len = (u64)-1; |
2356 | |||
2357 | if (!path) { | ||
2358 | path = btrfs_alloc_path(); | ||
2359 | BUG_ON(!path); | ||
2360 | } | ||
2361 | |||
2357 | ret = btrfs_lookup_file_extent(trans, root, path, | 2362 | ret = btrfs_lookup_file_extent(trans, root, path, |
2358 | objectid, start, trans != NULL); | 2363 | objectid, start, trans != NULL); |
2359 | if (ret < 0) { | 2364 | if (ret < 0) { |
@@ -2530,7 +2535,8 @@ insert: | |||
2530 | } | 2535 | } |
2531 | spin_unlock(&em_tree->lock); | 2536 | spin_unlock(&em_tree->lock); |
2532 | out: | 2537 | out: |
2533 | btrfs_free_path(path); | 2538 | if (path) |
2539 | btrfs_free_path(path); | ||
2534 | if (trans) { | 2540 | if (trans) { |
2535 | ret = btrfs_end_transaction(trans, root); | 2541 | ret = btrfs_end_transaction(trans, root); |
2536 | if (!err) { | 2542 | if (!err) { |
@@ -2643,8 +2649,8 @@ static int btrfs_writepage(struct page *page, struct writeback_control *wbc) | |||
2643 | return extent_write_full_page(tree, page, btrfs_get_extent, wbc); | 2649 | return extent_write_full_page(tree, page, btrfs_get_extent, wbc); |
2644 | } | 2650 | } |
2645 | 2651 | ||
2646 | static int btrfs_writepages(struct address_space *mapping, | 2652 | int btrfs_writepages(struct address_space *mapping, |
2647 | struct writeback_control *wbc) | 2653 | struct writeback_control *wbc) |
2648 | { | 2654 | { |
2649 | struct extent_io_tree *tree; | 2655 | struct extent_io_tree *tree; |
2650 | tree = &BTRFS_I(mapping->host)->io_tree; | 2656 | tree = &BTRFS_I(mapping->host)->io_tree; |