aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/inode.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs/inode.c')
-rw-r--r--fs/btrfs/inode.c76
1 files changed, 52 insertions, 24 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index dd91f4e60fb6..57a2bc25b4d2 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -839,6 +839,43 @@ static void free_async_extent_pages(struct async_extent *async_extent)
async_extent->pages = NULL;
}
+static int submit_uncompressed_range(struct btrfs_inode *inode,
+ struct async_extent *async_extent,
+ struct page *locked_page)
+{
+ u64 start = async_extent->start;
+ u64 end = async_extent->start + async_extent->ram_size - 1;
+ unsigned long nr_written = 0;
+ int page_started = 0;
+ int ret;
+
+ /*
+ * Call cow_file_range() to run the delalloc range directly, since we
+ * won't go to NOCOW or async path again.
+ *
+ * Also we call cow_file_range() with @unlock_page == 0, so that we
+ * can directly submit them without interruption.
+ */
+ ret = cow_file_range(inode, locked_page, start, end, &page_started,
+ &nr_written, 0);
+ /* Inline extent inserted, page gets unlocked and everything is done */
+ if (page_started) {
+ ret = 0;
+ goto out;
+ }
+ if (ret < 0) {
+ if (locked_page)
+ unlock_page(locked_page);
+ goto out;
+ }
+
+ ret = extent_write_locked_range(&inode->vfs_inode, start, end);
+ /* All pages will be unlocked, including @locked_page */
+out:
+ kfree(async_extent);
+ return ret;
+}
+
static int submit_one_async_extent(struct btrfs_inode *inode,
struct async_chunk *async_chunk,
struct async_extent *async_extent,
@@ -848,37 +885,28 @@ static int submit_one_async_extent(struct btrfs_inode *inode,
struct btrfs_root *root = inode->root;
struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_key ins;
+ struct page *locked_page = NULL;
struct extent_map *em;
int ret = 0;
u64 start = async_extent->start;
u64 end = async_extent->start + async_extent->ram_size - 1;
- lock_extent(io_tree, start, end);
-
- /* We have fallback to uncompressed write */
- if (!async_extent->pages) {
- int page_started = 0;
- unsigned long nr_written = 0;
+ /*
+ * If async_chunk->locked_page is in the async_extent range, we need to
+ * handle it.
+ */
+ if (async_chunk->locked_page) {
+ u64 locked_page_start = page_offset(async_chunk->locked_page);
+ u64 locked_page_end = locked_page_start + PAGE_SIZE - 1;
- /*
- * Call cow_file_range() to run the delalloc range directly,
- * since we won't go to nocow or async path again.
- */
- ret = cow_file_range(inode, async_chunk->locked_page,
- start, end, &page_started, &nr_written, 0);
- /*
- * If @page_started, cow_file_range() inserted an inline extent
- * and took care of all the unlocking and IO for us.
- * Otherwise, we need to submit all those pages down to the
- * drive.
- */
- if (!page_started && !ret)
- extent_write_locked_range(&inode->vfs_inode, start, end);
- else if (ret && async_chunk->locked_page)
- unlock_page(async_chunk->locked_page);
- kfree(async_extent);
- return ret;
+ if (!(start >= locked_page_end || end <= locked_page_start))
+ locked_page = async_chunk->locked_page;
}
+ lock_extent(io_tree, start, end);
+
+ /* We have fall back to uncompressed write */
+ if (!async_extent->pages)
+ return submit_uncompressed_range(inode, async_extent, locked_page);
ret = btrfs_reserve_extent(root, async_extent->ram_size,
async_extent->compressed_size,