Commit 77cef2ec authored by Josef Bacik's avatar Josef Bacik Committed by Chris Mason

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: default avatarJosef Bacik <jbacik@fusionio.com>
Signed-off-by: default avatarChris Mason <chris.mason@fusionio.com>
parent b12d6869
...@@ -2562,8 +2562,10 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent) ...@@ -2562,8 +2562,10 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
struct extent_state *cached_state = NULL; struct extent_state *cached_state = NULL;
struct new_sa_defrag_extent *new = NULL; struct new_sa_defrag_extent *new = NULL;
int compress_type = 0; int compress_type = 0;
int ret; int ret = 0;
u64 logical_len = ordered_extent->len;
bool nolock; bool nolock;
bool truncated = false;
nolock = btrfs_is_free_space_inode(inode); nolock = btrfs_is_free_space_inode(inode);
...@@ -2572,6 +2574,14 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent) ...@@ -2572,6 +2574,14 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
goto out; goto out;
} }
if (test_bit(BTRFS_ORDERED_TRUNCATED, &ordered_extent->flags)) {
truncated = true;
logical_len = ordered_extent->truncated_len;
/* Truncated the entire extent, don't bother adding */
if (!logical_len)
goto out;
}
if (test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags)) { if (test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags)) {
BUG_ON(!list_empty(&ordered_extent->list)); /* Logic error */ BUG_ON(!list_empty(&ordered_extent->list)); /* Logic error */
btrfs_ordered_update_i_size(inode, 0, ordered_extent); 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,15 +2637,14 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
ret = btrfs_mark_extent_written(trans, inode, ret = btrfs_mark_extent_written(trans, inode,
ordered_extent->file_offset, ordered_extent->file_offset,
ordered_extent->file_offset + ordered_extent->file_offset +
ordered_extent->len); logical_len);
} else { } else {
BUG_ON(root == root->fs_info->tree_root); BUG_ON(root == root->fs_info->tree_root);
ret = insert_reserved_file_extent(trans, inode, ret = insert_reserved_file_extent(trans, inode,
ordered_extent->file_offset, ordered_extent->file_offset,
ordered_extent->start, ordered_extent->start,
ordered_extent->disk_len, ordered_extent->disk_len,
ordered_extent->len, logical_len, logical_len,
ordered_extent->len,
compress_type, 0, 0, compress_type, 0, 0,
BTRFS_FILE_EXTENT_REG); BTRFS_FILE_EXTENT_REG);
} }
...@@ -2667,17 +2676,27 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent) ...@@ -2667,17 +2676,27 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
if (trans) if (trans)
btrfs_end_transaction(trans, root); btrfs_end_transaction(trans, root);
if (ret) { if (ret || truncated) {
clear_extent_uptodate(io_tree, ordered_extent->file_offset, u64 start, end;
ordered_extent->file_offset +
ordered_extent->len - 1, NULL, GFP_NOFS); if (truncated)
start = ordered_extent->file_offset + logical_len;
else
start = ordered_extent->file_offset;
end = ordered_extent->file_offset + ordered_extent->len - 1;
clear_extent_uptodate(io_tree, start, end, NULL, GFP_NOFS);
/* Drop the cache for the part of the extent we didn't write. */
btrfs_drop_extent_cache(inode, start, end, 0);
/* /*
* If the ordered extent had an IOERR or something else went * If the ordered extent had an IOERR or something else went
* wrong we need to return the space for this ordered extent * wrong we need to return the space for this ordered extent
* back to the allocator. * back to the allocator. We only free the extent in the
* truncated case if we didn't write out the extent at all.
*/ */
if (!test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags) && if ((ret || !logical_len) &&
!test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags) &&
!test_bit(BTRFS_ORDERED_PREALLOC, &ordered_extent->flags)) !test_bit(BTRFS_ORDERED_PREALLOC, &ordered_extent->flags))
btrfs_free_reserved_extent(root, ordered_extent->start, btrfs_free_reserved_extent(root, ordered_extent->start,
ordered_extent->disk_len); ordered_extent->disk_len);
...@@ -7336,10 +7355,23 @@ static void btrfs_invalidatepage(struct page *page, unsigned int offset, ...@@ -7336,10 +7355,23 @@ static void btrfs_invalidatepage(struct page *page, unsigned int offset,
* whoever cleared the private bit is responsible * whoever cleared the private bit is responsible
* for the finish_ordered_io * for the finish_ordered_io
*/ */
if (TestClearPagePrivate2(page) && if (TestClearPagePrivate2(page)) {
btrfs_dec_test_ordered_pending(inode, &ordered, page_start, struct btrfs_ordered_inode_tree *tree;
PAGE_CACHE_SIZE, 1)) { u64 new_len;
btrfs_finish_ordered_io(ordered);
tree = &BTRFS_I(inode)->ordered_tree;
spin_lock_irq(&tree->lock);
set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags);
new_len = page_start - ordered->file_offset;
if (new_len < ordered->truncated_len)
ordered->truncated_len = new_len;
spin_unlock_irq(&tree->lock);
if (btrfs_dec_test_ordered_pending(inode, &ordered,
page_start,
PAGE_CACHE_SIZE, 1))
btrfs_finish_ordered_io(ordered);
} }
btrfs_put_ordered_extent(ordered); btrfs_put_ordered_extent(ordered);
cached_state = NULL; cached_state = NULL;
......
...@@ -205,6 +205,7 @@ static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset, ...@@ -205,6 +205,7 @@ static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
entry->bytes_left = len; entry->bytes_left = len;
entry->inode = igrab(inode); entry->inode = igrab(inode);
entry->compress_type = compress_type; entry->compress_type = compress_type;
entry->truncated_len = (u64)-1;
if (type != BTRFS_ORDERED_IO_DONE && type != BTRFS_ORDERED_COMPLETE) if (type != BTRFS_ORDERED_IO_DONE && type != BTRFS_ORDERED_COMPLETE)
set_bit(type, &entry->flags); set_bit(type, &entry->flags);
...@@ -920,12 +921,16 @@ int btrfs_ordered_update_i_size(struct inode *inode, u64 offset, ...@@ -920,12 +921,16 @@ int btrfs_ordered_update_i_size(struct inode *inode, u64 offset,
struct btrfs_ordered_extent *test; struct btrfs_ordered_extent *test;
int ret = 1; int ret = 1;
if (ordered) spin_lock_irq(&tree->lock);
if (ordered) {
offset = entry_end(ordered); offset = entry_end(ordered);
else if (test_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags))
offset = min(offset,
ordered->file_offset +
ordered->truncated_len);
} else {
offset = ALIGN(offset, BTRFS_I(inode)->root->sectorsize); offset = ALIGN(offset, BTRFS_I(inode)->root->sectorsize);
}
spin_lock_irq(&tree->lock);
disk_i_size = BTRFS_I(inode)->disk_i_size; disk_i_size = BTRFS_I(inode)->disk_i_size;
/* truncate file */ /* truncate file */
......
...@@ -69,6 +69,7 @@ struct btrfs_ordered_sum { ...@@ -69,6 +69,7 @@ struct btrfs_ordered_sum {
* the isize. */ * the isize. */
#define BTRFS_ORDERED_LOGGED_CSUM 8 /* We've logged the csums on this ordered #define BTRFS_ORDERED_LOGGED_CSUM 8 /* We've logged the csums on this ordered
ordered extent */ ordered extent */
#define BTRFS_ORDERED_TRUNCATED 9 /* Set when we have to truncate an extent */
struct btrfs_ordered_extent { struct btrfs_ordered_extent {
/* logical offset in the file */ /* logical offset in the file */
...@@ -96,6 +97,12 @@ struct btrfs_ordered_extent { ...@@ -96,6 +97,12 @@ struct btrfs_ordered_extent {
*/ */
u64 outstanding_isize; u64 outstanding_isize;
/*
* If we get truncated we need to adjust the file extent we enter for
* this ordered extent so that we do not expose stale data.
*/
u64 truncated_len;
/* flags (described above) */ /* flags (described above) */
unsigned long flags; unsigned long flags;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment