aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@fusionio.com>2013-08-29 13:57:21 -0400
committerChris Mason <chris.mason@fusionio.com>2013-09-01 08:16:34 -0400
commit77cef2ec5484564eca6bd12a2b4a1e88fd766fbc (patch)
treeedc2b57493ccebe4dc4ad48daf03a2ac25f24a10
parentb12d6869f67a95692017d26313ea5736d4043d0f (diff)
Btrfs: allow partial ordered extent completion
We currently have this problem where you can truncate pages that have not yet been written for an ordered extent. We do this because the truncate will be coming behind to clean us up anyway so what's the harm right? Well if truncate fails for whatever reason we leave an orphan item around for the file to be cleaned up later. But if the user goes and truncates up the file and tries to read from the area that had been discarded previously they will get a csum error because we never actually wrote that data out. This patch fixes this by allowing us to either discard the ordered extent completely, by which I mean we just free up the space we had allocated and not add the file extent, or adjust the length of the file extent we write. We do this by setting the length we truncated down to in the ordered extent, and then we set the file extent length and ram bytes to this length. The total disk space stays unchanged since we may be compressed and we can't just chop off the disk space, but at least this way the file extent only points to the valid data. Then when the file extent is free'd the extent and csums will be freed normally. This patch is needed for the next series which will give us more graceful recovery of failed truncates. Thanks, Signed-off-by: Josef Bacik <jbacik@fusionio.com> Signed-off-by: Chris Mason <chris.mason@fusionio.com>
-rw-r--r--fs/btrfs/inode.c60
-rw-r--r--fs/btrfs/ordered-data.c13
-rw-r--r--fs/btrfs/ordered-data.h7
3 files changed, 62 insertions, 18 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 4d4e2de3e879..f0e41b840739 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -2562,8 +2562,10 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
2562 struct extent_state *cached_state = NULL; 2562 struct extent_state *cached_state = NULL;
2563 struct new_sa_defrag_extent *new = NULL; 2563 struct new_sa_defrag_extent *new = NULL;
2564 int compress_type = 0; 2564 int compress_type = 0;
2565 int ret; 2565 int ret = 0;
2566 u64 logical_len = ordered_extent->len;
2566 bool nolock; 2567 bool nolock;
2568 bool truncated = false;
2567 2569
2568 nolock = btrfs_is_free_space_inode(inode); 2570 nolock = btrfs_is_free_space_inode(inode);
2569 2571
@@ -2572,6 +2574,14 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
2572 goto out; 2574 goto out;
2573 } 2575 }
2574 2576
2577 if (test_bit(BTRFS_ORDERED_TRUNCATED, &ordered_extent->flags)) {
2578 truncated = true;
2579 logical_len = ordered_extent->truncated_len;
2580 /* Truncated the entire extent, don't bother adding */
2581 if (!logical_len)
2582 goto out;
2583 }
2584
2575 if (test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags)) { 2585 if (test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags)) {
2576 BUG_ON(!list_empty(&ordered_extent->list)); /* Logic error */ 2586 BUG_ON(!list_empty(&ordered_extent->list)); /* Logic error */
2577 btrfs_ordered_update_i_size(inode, 0, ordered_extent); 2587 btrfs_ordered_update_i_size(inode, 0, ordered_extent);
@@ -2627,15 +2637,14 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
2627 ret = btrfs_mark_extent_written(trans, inode, 2637 ret = btrfs_mark_extent_written(trans, inode,
2628 ordered_extent->file_offset, 2638 ordered_extent->file_offset,
2629 ordered_extent->file_offset + 2639 ordered_extent->file_offset +
2630 ordered_extent->len); 2640 logical_len);
2631 } else { 2641 } else {
2632 BUG_ON(root == root->fs_info->tree_root); 2642 BUG_ON(root == root->fs_info->tree_root);
2633 ret = insert_reserved_file_extent(trans, inode, 2643 ret = insert_reserved_file_extent(trans, inode,
2634 ordered_extent->file_offset, 2644 ordered_extent->file_offset,
2635 ordered_extent->start, 2645 ordered_extent->start,
2636 ordered_extent->disk_len, 2646 ordered_extent->disk_len,
2637 ordered_extent->len, 2647 logical_len, logical_len,
2638 ordered_extent->len,
2639 compress_type, 0, 0, 2648 compress_type, 0, 0,
2640 BTRFS_FILE_EXTENT_REG); 2649 BTRFS_FILE_EXTENT_REG);
2641 } 2650 }
@@ -2667,17 +2676,27 @@ out:
2667 if (trans) 2676 if (trans)
2668 btrfs_end_transaction(trans, root); 2677 btrfs_end_transaction(trans, root);
2669 2678
2670 if (ret) { 2679 if (ret || truncated) {
2671 clear_extent_uptodate(io_tree, ordered_extent->file_offset, 2680 u64 start, end;
2672 ordered_extent->file_offset + 2681
2673 ordered_extent->len - 1, NULL, GFP_NOFS); 2682 if (truncated)
2683 start = ordered_extent->file_offset + logical_len;
2684 else
2685 start = ordered_extent->file_offset;
2686 end = ordered_extent->file_offset + ordered_extent->len - 1;
2687 clear_extent_uptodate(io_tree, start, end, NULL, GFP_NOFS);
2688
2689 /* Drop the cache for the part of the extent we didn't write. */
2690 btrfs_drop_extent_cache(inode, start, end, 0);
2674 2691
2675 /* 2692 /*
2676 * If the ordered extent had an IOERR or something else went 2693 * If the ordered extent had an IOERR or something else went
2677 * wrong we need to return the space for this ordered extent 2694 * wrong we need to return the space for this ordered extent
2678 * back to the allocator. 2695 * back to the allocator. We only free the extent in the
2696 * truncated case if we didn't write out the extent at all.
2679 */ 2697 */
2680 if (!test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags) && 2698 if ((ret || !logical_len) &&
2699 !test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags) &&
2681 !test_bit(BTRFS_ORDERED_PREALLOC, &ordered_extent->flags)) 2700 !test_bit(BTRFS_ORDERED_PREALLOC, &ordered_extent->flags))
2682 btrfs_free_reserved_extent(root, ordered_extent->start, 2701 btrfs_free_reserved_extent(root, ordered_extent->start,
2683 ordered_extent->disk_len); 2702 ordered_extent->disk_len);
@@ -7336,10 +7355,23 @@ static void btrfs_invalidatepage(struct page *page, unsigned int offset,
7336 * whoever cleared the private bit is responsible 7355 * whoever cleared the private bit is responsible
7337 * for the finish_ordered_io 7356 * for the finish_ordered_io
7338 */ 7357 */
7339 if (TestClearPagePrivate2(page) && 7358 if (TestClearPagePrivate2(page)) {
7340 btrfs_dec_test_ordered_pending(inode, &ordered, page_start, 7359 struct btrfs_ordered_inode_tree *tree;
7341 PAGE_CACHE_SIZE, 1)) { 7360 u64 new_len;
7342 btrfs_finish_ordered_io(ordered); 7361
7362 tree = &BTRFS_I(inode)->ordered_tree;
7363
7364 spin_lock_irq(&tree->lock);
7365 set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags);
7366 new_len = page_start - ordered->file_offset;
7367 if (new_len < ordered->truncated_len)
7368 ordered->truncated_len = new_len;
7369 spin_unlock_irq(&tree->lock);
7370
7371 if (btrfs_dec_test_ordered_pending(inode, &ordered,
7372 page_start,
7373 PAGE_CACHE_SIZE, 1))
7374 btrfs_finish_ordered_io(ordered);
7343 } 7375 }
7344 btrfs_put_ordered_extent(ordered); 7376 btrfs_put_ordered_extent(ordered);
7345 cached_state = NULL; 7377 cached_state = NULL;
diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c
index f2023ccb7cf6..966b413a33b8 100644
--- a/fs/btrfs/ordered-data.c
+++ b/fs/btrfs/ordered-data.c
@@ -205,6 +205,7 @@ static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
205 entry->bytes_left = len; 205 entry->bytes_left = len;
206 entry->inode = igrab(inode); 206 entry->inode = igrab(inode);
207 entry->compress_type = compress_type; 207 entry->compress_type = compress_type;
208 entry->truncated_len = (u64)-1;
208 if (type != BTRFS_ORDERED_IO_DONE && type != BTRFS_ORDERED_COMPLETE) 209 if (type != BTRFS_ORDERED_IO_DONE && type != BTRFS_ORDERED_COMPLETE)
209 set_bit(type, &entry->flags); 210 set_bit(type, &entry->flags);
210 211
@@ -920,12 +921,16 @@ int btrfs_ordered_update_i_size(struct inode *inode, u64 offset,
920 struct btrfs_ordered_extent *test; 921 struct btrfs_ordered_extent *test;
921 int ret = 1; 922 int ret = 1;
922 923
923 if (ordered) 924 spin_lock_irq(&tree->lock);
925 if (ordered) {
924 offset = entry_end(ordered); 926 offset = entry_end(ordered);
925 else 927 if (test_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags))
928 offset = min(offset,
929 ordered->file_offset +
930 ordered->truncated_len);
931 } else {
926 offset = ALIGN(offset, BTRFS_I(inode)->root->sectorsize); 932 offset = ALIGN(offset, BTRFS_I(inode)->root->sectorsize);
927 933 }
928 spin_lock_irq(&tree->lock);
929 disk_i_size = BTRFS_I(inode)->disk_i_size; 934 disk_i_size = BTRFS_I(inode)->disk_i_size;
930 935
931 /* truncate file */ 936 /* truncate file */
diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h
index 68844d59ee6f..d9a5aa097b4f 100644
--- a/fs/btrfs/ordered-data.h
+++ b/fs/btrfs/ordered-data.h
@@ -69,6 +69,7 @@ struct btrfs_ordered_sum {
69 * the isize. */ 69 * the isize. */
70#define BTRFS_ORDERED_LOGGED_CSUM 8 /* We've logged the csums on this ordered 70#define BTRFS_ORDERED_LOGGED_CSUM 8 /* We've logged the csums on this ordered
71 ordered extent */ 71 ordered extent */
72#define BTRFS_ORDERED_TRUNCATED 9 /* Set when we have to truncate an extent */
72 73
73struct btrfs_ordered_extent { 74struct btrfs_ordered_extent {
74 /* logical offset in the file */ 75 /* logical offset in the file */
@@ -96,6 +97,12 @@ struct btrfs_ordered_extent {
96 */ 97 */
97 u64 outstanding_isize; 98 u64 outstanding_isize;
98 99
100 /*
101 * If we get truncated we need to adjust the file extent we enter for
102 * this ordered extent so that we do not expose stale data.
103 */
104 u64 truncated_len;
105
99 /* flags (described above) */ 106 /* flags (described above) */
100 unsigned long flags; 107 unsigned long flags;
101 108