[PATCH 10/21] btrfs: lock extents before pages in writepages

Goldwyn Rodrigues
2023-03-02T22:25:31Z

writepages() locks the extents in find_lock_delalloc_range() and unlocks
using clear_bit EXTENT_LOCKED operations is cow/delalloc operations.

Call extent locking/unlocking around writepages() sequence as opposed to
while performing delayed allocation.

This converts a range_cyclic wbc to non-range_cyclic wbc with the range
set to start from writeback_index and ending at inode size. This is done
because inode size can change while the writepages() is going on. So,
the number of pages accounted for writeback in wbc are accurate.

Signed-off-by: Goldwyn Rodrigues <rgoldwyn@suse.com>
---
fs/btrfs/extent_io.c | 5 ----
fs/btrfs/free-space-cache.c | 2 +-
fs/btrfs/inode.c | 53 ++++++++++++++++++++++++++++---------
3 files changed, 42 insertions(+), 18 deletions(-)

diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index e44329a84caf..cdce2db82d7e 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -483,15 +483,10 @@ noinline_for_stack bool find_lock_delalloc_range(struct inode *inode,
}
}

- /* step three, lock the state bits for the whole range */
- lock_extent(tree, delalloc_start, delalloc_end, &cached_state);
-
/* then test to make sure it is all still delalloc */
ret = test_range_bit(tree, delalloc_start, delalloc_end,
EXTENT_DELALLOC, 1, cached_state);
if (!ret) {
- unlock_extent(tree, delalloc_start, delalloc_end,
- &cached_state);
__unlock_for_delalloc(inode, locked_page,
delalloc_start, delalloc_end);
cond_resched();
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index 0d250d052487..2373f248d70f 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -355,9 +355,9 @@ int btrfs_truncate_free_space_cache(struct btrfs_trans_handle *trans,
}

btrfs_i_size_write(inode, 0);
- truncate_pagecache(vfs_inode, 0);

lock_extent(&inode->io_tree, 0, (u64)-1, &cached_state);
+ truncate_pagecache(vfs_inode, 0);
btrfs_drop_extent_map_range(inode, 0, (u64)-1, false);

/*
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 53bd9a64e803..eeddd7cdff58 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -977,7 +977,6 @@ static int submit_one_async_extent(struct btrfs_inode *inode,
struct async_extent *async_extent,
u64 *alloc_hint)
{
- struct extent_io_tree *io_tree = &inode->io_tree;
struct btrfs_root *root = inode->root;
struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_key ins;
@@ -998,7 +997,6 @@ static int submit_one_async_extent(struct btrfs_inode *inode,
if (!(start >= locked_page_end || end <= locked_page_start))
locked_page = async_chunk->locked_page;
}
- lock_extent(io_tree, start, end, NULL);

/* We have fall back to uncompressed write */
if (!async_extent->pages)
@@ -1052,7 +1050,7 @@ static int submit_one_async_extent(struct btrfs_inode *inode,

/* Clear dirty, set writeback and unlock the pages. */
extent_clear_unlock_delalloc(inode, start, end,
- NULL, EXTENT_LOCKED | EXTENT_DELALLOC,
+ NULL, EXTENT_DELALLOC,
PAGE_UNLOCK | PAGE_START_WRITEBACK);
if (btrfs_submit_compressed_write(inode, start, /* file_offset */
async_extent->ram_size, /* num_bytes */
@@ -1080,7 +1078,7 @@ static int submit_one_async_extent(struct btrfs_inode *inode,
btrfs_free_reserved_extent(fs_info, ins.objectid, ins.offset, 1);
out_free:
extent_clear_unlock_delalloc(inode, start, end,
- NULL, EXTENT_LOCKED | EXTENT_DELALLOC |
+ NULL, EXTENT_DELALLOC |
EXTENT_DELALLOC_NEW |
EXTENT_DEFRAG | EXTENT_DO_ACCOUNTING,
PAGE_UNLOCK | PAGE_START_WRITEBACK |
@@ -1248,7 +1246,7 @@ static noinline int cow_file_range(struct btrfs_inode *inode,
*/
extent_clear_unlock_delalloc(inode, start, end,
locked_page,
- EXTENT_LOCKED | EXTENT_DELALLOC |
+ EXTENT_DELALLOC |
EXTENT_DELALLOC_NEW | EXTENT_DEFRAG |
EXTENT_DO_ACCOUNTING, PAGE_UNLOCK |
PAGE_START_WRITEBACK | PAGE_END_WRITEBACK);
@@ -1359,7 +1357,7 @@ static noinline int cow_file_range(struct btrfs_inode *inode,

extent_clear_unlock_delalloc(inode, start, start + ram_size - 1,
locked_page,
- EXTENT_LOCKED | EXTENT_DELALLOC,
+ EXTENT_DELALLOC,
page_ops);
if (num_bytes < cur_alloc_size)
num_bytes = 0;
@@ -1410,7 +1408,7 @@ static noinline int cow_file_range(struct btrfs_inode *inode,
* We process each region below.
*/

- clear_bits = EXTENT_LOCKED | EXTENT_DELALLOC | EXTENT_DELALLOC_NEW |
+ clear_bits = EXTENT_DELALLOC | EXTENT_DELALLOC_NEW |
EXTENT_DEFRAG | EXTENT_CLEAR_META_RESV;
page_ops = PAGE_UNLOCK | PAGE_START_WRITEBACK | PAGE_END_WRITEBACK;

@@ -1560,7 +1558,7 @@ static int cow_file_range_async(struct btrfs_inode *inode,
memalloc_nofs_restore(nofs_flag);

if (!ctx) {
- unsigned clear_bits = EXTENT_LOCKED | EXTENT_DELALLOC |
+ unsigned clear_bits = EXTENT_DELALLOC |
EXTENT_DELALLOC_NEW | EXTENT_DEFRAG |
EXTENT_DO_ACCOUNTING;
unsigned long page_ops = PAGE_UNLOCK | PAGE_START_WRITEBACK |
@@ -1940,7 +1938,7 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
path = btrfs_alloc_path();
if (!path) {
extent_clear_unlock_delalloc(inode, start, end, locked_page,
- EXTENT_LOCKED | EXTENT_DELALLOC |
+ EXTENT_DELALLOC |
EXTENT_DO_ACCOUNTING |
EXTENT_DEFRAG, PAGE_UNLOCK |
PAGE_START_WRITEBACK |
@@ -2154,7 +2152,7 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
nocow_args.num_bytes);

extent_clear_unlock_delalloc(inode, cur_offset, nocow_end,
- locked_page, EXTENT_LOCKED |
+ locked_page,
EXTENT_DELALLOC |
EXTENT_CLEAR_DATA_RESV,
PAGE_UNLOCK | PAGE_SET_ORDERED);
@@ -2190,7 +2188,7 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,

if (ret && cur_offset < end)
extent_clear_unlock_delalloc(inode, cur_offset, end,
- locked_page, EXTENT_LOCKED |
+ locked_page,
EXTENT_DELALLOC | EXTENT_DEFRAG |
EXTENT_DO_ACCOUNTING, PAGE_UNLOCK |
PAGE_START_WRITEBACK |
@@ -7845,7 +7843,38 @@ static int btrfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
static int btrfs_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
- return extent_writepages(mapping, wbc);
+ u64 start = 0, end = LLONG_MAX;
+ struct inode *inode = mapping->host;
+ struct extent_state *cached = NULL;
+ int ret;
+ loff_t isize = i_size_read(inode);
+ struct writeback_control new_wbc = *wbc;
+
+ if (new_wbc.range_cyclic) {
+ start = mapping->writeback_index << PAGE_SHIFT;
+ end = round_up(isize, PAGE_SIZE) - 1;
+ wbc->range_cyclic = 0;
+ wbc->range_start = start;
+ wbc->range_end = end;
+ } else {
+ start = round_down(wbc->range_start, PAGE_SIZE);
+ end = round_up(wbc->range_end, PAGE_SIZE) - 1;
+ }
+
+ if (start >= end)
+ return 0;
+
+ lock_extent(&BTRFS_I(inode)->io_tree, start, end, &cached);
+ ret = extent_writepages(mapping, wbc);
+ unlock_extent(&BTRFS_I(inode)->io_tree, start, end, &cached);
+
+ if (new_wbc.range_cyclic) {
+ wbc->range_start = new_wbc.range_start;
+ wbc->range_end = new_wbc.range_end;
+ wbc->range_cyclic = 1;
+ }
+
+ return ret;
}

static void btrfs_readahead_begin(struct readahead_control *rac)
--
2.39.2