summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/btrfs/extent-io-tree.c14
-rw-r--r--fs/btrfs/extent-io-tree.h3
-rw-r--r--fs/btrfs/extent_io.c18
3 files changed, 26 insertions, 9 deletions
diff --git a/fs/btrfs/extent-io-tree.c b/fs/btrfs/extent-io-tree.c
index 14510a71a8fd..7ae24a533404 100644
--- a/fs/btrfs/extent-io-tree.c
+++ b/fs/btrfs/extent-io-tree.c
@@ -1752,14 +1752,26 @@ bool test_range_bit_exists(struct extent_io_tree *tree, u64 start, u64 end, u32
return bitset;
}
-void get_range_bits(struct extent_io_tree *tree, u64 start, u64 end, u32 *bits)
+void get_range_bits(struct extent_io_tree *tree, u64 start, u64 end, u32 *bits,
+ struct extent_state **cached_state)
{
struct extent_state *state;
+ /*
+ * The cached state is currently mandatory and not used to start the
+ * search, only to cache the first state record found in the range.
+ */
+ ASSERT(cached_state != NULL);
+ ASSERT(*cached_state == NULL);
+
*bits = 0;
spin_lock(&tree->lock);
state = tree_search(tree, start);
+ if (state && state->start < end) {
+ *cached_state = state;
+ refcount_inc(&state->refs);
+ }
while (state) {
if (state->start > end)
break;
diff --git a/fs/btrfs/extent-io-tree.h b/fs/btrfs/extent-io-tree.h
index 9095e49e059c..6dfe8b097d93 100644
--- a/fs/btrfs/extent-io-tree.h
+++ b/fs/btrfs/extent-io-tree.h
@@ -170,7 +170,8 @@ void free_extent_state(struct extent_state *state);
bool test_range_bit(struct extent_io_tree *tree, u64 start, u64 end, u32 bit,
struct extent_state *cached_state);
bool test_range_bit_exists(struct extent_io_tree *tree, u64 start, u64 end, u32 bit);
-void get_range_bits(struct extent_io_tree *tree, u64 start, u64 end, u32 *bits);
+void get_range_bits(struct extent_io_tree *tree, u64 start, u64 end, u32 *bits,
+ struct extent_state **cached_state);
int clear_record_extent_bits(struct extent_io_tree *tree, u64 start, u64 end,
u32 bits, struct extent_changeset *changeset);
int __clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 8a544c6e65d9..a9ecf10debd9 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -2621,13 +2621,15 @@ int extent_invalidate_folio(struct extent_io_tree *tree,
static bool try_release_extent_state(struct extent_io_tree *tree,
struct folio *folio)
{
+ struct extent_state *cached_state = NULL;
u64 start = folio_pos(folio);
u64 end = start + folio_size(folio) - 1;
u32 range_bits;
u32 clear_bits;
- int ret;
+ bool ret = false;
+ int ret2;
- get_range_bits(tree, start, end, &range_bits);
+ get_range_bits(tree, start, end, &range_bits, &cached_state);
/*
* We can release the folio if it's locked only for ordered extent
@@ -2635,7 +2637,7 @@ static bool try_release_extent_state(struct extent_io_tree *tree,
*/
if ((range_bits & EXTENT_LOCKED) &&
!(range_bits & EXTENT_FINISHING_ORDERED))
- return false;
+ goto out;
clear_bits = ~(EXTENT_LOCKED | EXTENT_NODATASUM | EXTENT_DELALLOC_NEW |
EXTENT_CTLBITS | EXTENT_QGROUP_RESERVED |
@@ -2645,15 +2647,17 @@ static bool try_release_extent_state(struct extent_io_tree *tree,
* nodatasum, delalloc new and finishing ordered bits. The delalloc new
* bit will be cleared by ordered extent completion.
*/
- ret = __clear_extent_bit(tree, start, end, clear_bits, NULL, NULL);
+ ret2 = __clear_extent_bit(tree, start, end, clear_bits, &cached_state, NULL);
/*
* If clear_extent_bit failed for enomem reasons, we can't allow the
* release to continue.
*/
- if (ret < 0)
- return false;
+ if (ret2 == 0)
+ ret = true;
+out:
+ free_extent_state(cached_state);
- return true;
+ return ret;
}
/*