diff mbox series

[06/17] btrfs: push the extent lock into btrfs_run_delalloc_range

Message ID 1d2952b7fccde719e25867471e61a0126e77e3b6.1713363472.git.josef@toxicpanda.com (mailing list archive)
State New
Headers show
Series btrfs: restrain lock extent usage during writeback | expand

Commit Message

Josef Bacik April 17, 2024, 2:35 p.m. UTC
We want to limit the scope of the extent lock to be around operations
that can change in flight.  Currently we hold the extent lock through
the entire writepage operation, which isn't really necessary.

We want to protect to make sure nobody has updated DELALLOC.  In
find_lock_delalloc_range we must lock the range in order to validate the
contents of our io_tree.  However once we've done that we're safe to
unlock the range and continue, as we have the page lock already held for
the range.

We are protected from all operations at this point.

* mmap() - we're holding the page lock, thus are protected.
* buffered writes - again, we're protected because we take the page lock
  for the first and last page in our range for buffered writes so we
  won't create new delalloc ranges in this area.
* direct IO - we invalidate pagecache before attempting to write a new
  area, which requires the page lock, so again are protected once we're
  holding the page lock on this range.

Additionally this behavior actually already exists for compressed, we
unlock the range as soon as we start to process the async extents, and
re-lock it during compression.  So this is completely safe, and makes
the locking more consistent.

Make this simple by just pushing the extent lock into
btrfs_run_delalloc_range.  From there followup patches will push the
lock further down into its users.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/extent_io.c | 5 ++---
 fs/btrfs/inode.c     | 5 +++++
 2 files changed, 7 insertions(+), 3 deletions(-)

Comments

Goldwyn Rodrigues April 24, 2024, 5:26 p.m. UTC | #1
On 10:35 17/04, Josef Bacik wrote:
> We want to limit the scope of the extent lock to be around operations
> that can change in flight.  Currently we hold the extent lock through
> the entire writepage operation, which isn't really necessary.
> 
> We want to protect to make sure nobody has updated DELALLOC.  In
> find_lock_delalloc_range we must lock the range in order to validate the
> contents of our io_tree.  However once we've done that we're safe to
> unlock the range and continue, as we have the page lock already held for
> the range.
> 
> We are protected from all operations at this point.
> 
> * mmap() - we're holding the page lock, thus are protected.
> * buffered writes - again, we're protected because we take the page lock
>   for the first and last page in our range for buffered writes so we
>   won't create new delalloc ranges in this area.
> * direct IO - we invalidate pagecache before attempting to write a new
>   area, which requires the page lock, so again are protected once we're
>   holding the page lock on this range.
> 
> Additionally this behavior actually already exists for compressed, we
> unlock the range as soon as we start to process the async extents, and
> re-lock it during compression.  So this is completely safe, and makes
> the locking more consistent.
> 
> Make this simple by just pushing the extent lock into
> btrfs_run_delalloc_range.  From there followup patches will push the
> lock further down into its users.
> 
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>

Reviewed-by: Goldwyn Rodrigues <rgoldwyn@suse.com>
diff mbox series

Patch

diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 7b10f47d8f83..c09f46f969b1 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -396,15 +396,14 @@  noinline_for_stack bool find_lock_delalloc_range(struct inode *inode,
 	/* then test to make sure it is all still delalloc */
 	ret = test_range_bit(tree, delalloc_start, delalloc_end,
 			     EXTENT_DELALLOC, cached_state);
+
+	unlock_extent(tree, delalloc_start, delalloc_end, &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();
 		goto again;
 	}
-	free_extent_state(cached_state);
 	*start = delalloc_start;
 	*end = delalloc_end;
 out_failed:
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index ba74476ac423..2083005f2828 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -2249,6 +2249,11 @@  int btrfs_run_delalloc_range(struct btrfs_inode *inode, struct page *locked_page
 	const bool zoned = btrfs_is_zoned(inode->root->fs_info);
 	int ret;
 
+	/*
+	 * We're unlocked by the different fill functions below.
+	 */
+	lock_extent(&inode->io_tree, start, end, NULL);
+
 	/*
 	 * The range must cover part of the @locked_page, or a return of 1
 	 * can confuse the caller.