|
@@ -2562,8 +2562,10 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
|
|
|
struct extent_state *cached_state = NULL;
|
|
|
struct new_sa_defrag_extent *new = NULL;
|
|
|
int compress_type = 0;
|
|
|
- int ret;
|
|
|
+ int ret = 0;
|
|
|
+ u64 logical_len = ordered_extent->len;
|
|
|
bool nolock;
|
|
|
+ bool truncated = false;
|
|
|
|
|
|
nolock = btrfs_is_free_space_inode(inode);
|
|
|
|
|
@@ -2572,6 +2574,14 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
|
|
|
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)) {
|
|
|
BUG_ON(!list_empty(&ordered_extent->list)); /* Logic error */
|
|
|
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)
|
|
|
ret = btrfs_mark_extent_written(trans, inode,
|
|
|
ordered_extent->file_offset,
|
|
|
ordered_extent->file_offset +
|
|
|
- ordered_extent->len);
|
|
|
+ logical_len);
|
|
|
} else {
|
|
|
BUG_ON(root == root->fs_info->tree_root);
|
|
|
ret = insert_reserved_file_extent(trans, inode,
|
|
|
ordered_extent->file_offset,
|
|
|
ordered_extent->start,
|
|
|
ordered_extent->disk_len,
|
|
|
- ordered_extent->len,
|
|
|
- ordered_extent->len,
|
|
|
+ logical_len, logical_len,
|
|
|
compress_type, 0, 0,
|
|
|
BTRFS_FILE_EXTENT_REG);
|
|
|
}
|
|
@@ -2667,17 +2676,27 @@ out:
|
|
|
if (trans)
|
|
|
btrfs_end_transaction(trans, root);
|
|
|
|
|
|
- if (ret) {
|
|
|
- clear_extent_uptodate(io_tree, ordered_extent->file_offset,
|
|
|
- ordered_extent->file_offset +
|
|
|
- ordered_extent->len - 1, NULL, GFP_NOFS);
|
|
|
+ if (ret || truncated) {
|
|
|
+ u64 start, end;
|
|
|
+
|
|
|
+ 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
|
|
|
* 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))
|
|
|
btrfs_free_reserved_extent(root, ordered_extent->start,
|
|
|
ordered_extent->disk_len);
|
|
@@ -7336,10 +7355,23 @@ static void btrfs_invalidatepage(struct page *page, unsigned int offset,
|
|
|
* whoever cleared the private bit is responsible
|
|
|
* for the finish_ordered_io
|
|
|
*/
|
|
|
- if (TestClearPagePrivate2(page) &&
|
|
|
- btrfs_dec_test_ordered_pending(inode, &ordered, page_start,
|
|
|
- PAGE_CACHE_SIZE, 1)) {
|
|
|
- btrfs_finish_ordered_io(ordered);
|
|
|
+ if (TestClearPagePrivate2(page)) {
|
|
|
+ struct btrfs_ordered_inode_tree *tree;
|
|
|
+ u64 new_len;
|
|
|
+
|
|
|
+ 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);
|
|
|
cached_state = NULL;
|