All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6] btrfs: allocator perf improvements
@ 2026-09-29  0:28 Boris Burkov
  2026-09-29  0:28 ` [PATCH 1/6] btrfs: skip unhelpful size class relaxing allocation loops Boris Burkov
                   ` (6 more replies)
  0 siblings, 7 replies; 10+ messages in thread
From: Boris Burkov @ 2026-09-29  0:28 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

Working on the bugs to do with large fs-es and unused block groups

https://lore.kernel.org/linux-btrfs/20260916230447.GA1706558@zen.localdomain/T/#t
and
https://lore.kernel.org/linux-btrfs/cover.1790641111.git.boris@bur.io/T/#t

revealed some low hanging fruit in allocation performance for very large
mostly full fs-es. The gist is to try to skip unnecessary operations per block
group which adds up with 10s of thousands of obviously skippable block
groups. Running 20k fallocates in a loop on a sparse file fs improved from
~40s to ~7s.

Boris Burkov (6):
  btrfs: skip unhelpful size class relaxing allocation loops
  btrfs: don't take block group references in find_free_extent()
  btrfs: remove redundant checks in find_free_extent() hint path
  btrfs: factor the find_free_extent() block group skip into a helper
  btrfs: find_free_extent checks full block groups unlocked
  btrfs: find_free_extent checks size class unlocked

 fs/btrfs/extent-tree.c | 88 +++++++++++++++++++++++++++++-------------
 fs/btrfs/extent-tree.h |  3 ++
 2 files changed, 65 insertions(+), 26 deletions(-)

-- 
2.55.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/6] btrfs: skip unhelpful size class relaxing allocation loops
  2026-09-29  0:28 [PATCH 0/6] btrfs: allocator perf improvements Boris Burkov
@ 2026-09-29  0:28 ` Boris Burkov
  2026-09-29  0:28 ` [PATCH 2/6] btrfs: don't take block group references in find_free_extent() Boris Burkov
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Boris Burkov @ 2026-09-29  0:28 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

When size class was not a factor on a previous loop, there is no real
point to running a loop with relaxed size class rules. So track whether
we ever actually skipped a bg due to size class and only do those loops
when it is relevant.

Signed-off-by: Boris Burkov <boris@bur.io>
---
 fs/btrfs/extent-tree.c | 12 +++++++++++-
 fs/btrfs/extent-tree.h |  3 +++
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index a0d5ab03aae2..f43ee0ab24bc 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -4365,6 +4365,9 @@ static int find_free_extent_update_loop(struct btrfs_fs_info *fs_info,
 	    (!ffe_ctl->orig_have_caching_bg && full_search))
 		ffe_ctl->loop++;
 	ffe_ctl->loop++;
+	/* Relaxing the size class cannot help if no block group was skipped for it. */
+	if (ffe_ctl->loop == LOOP_UNSET_SIZE_CLASS && !ffe_ctl->size_class_skipped)
+		ffe_ctl->loop++;
 
 	if (ffe_ctl->loop == LOOP_ALLOC_CHUNK) {
 		struct btrfs_trans_handle *trans;
@@ -4404,6 +4407,9 @@ static int find_free_extent_update_loop(struct btrfs_fs_info *fs_info,
 			return ret;
 	}
 
+	if (ffe_ctl->loop == LOOP_WRONG_SIZE_CLASS && !ffe_ctl->size_class_skipped)
+		ffe_ctl->loop++;
+
 	if (ffe_ctl->loop == LOOP_NO_EMPTY_SIZE) {
 		if (ffe_ctl->policy != BTRFS_EXTENT_ALLOC_CLUSTERED)
 			return -ENOSPC;
@@ -4418,6 +4424,7 @@ static int find_free_extent_update_loop(struct btrfs_fs_info *fs_info,
 		ffe_ctl->empty_cluster = 0;
 	}
 
+	ffe_ctl->size_class_skipped = false;
 	return 1;
 }
 
@@ -4581,6 +4588,7 @@ static noinline int find_free_extent(struct btrfs_root *root,
 	ffe_ctl->index = btrfs_bg_flags_to_raid_index(ffe_ctl->flags);
 	ffe_ctl->loop = 0;
 	ffe_ctl->retry_uncached = false;
+	ffe_ctl->size_class_skipped = false;
 	ffe_ctl->cached = 0;
 	ffe_ctl->max_extent_size = 0;
 	ffe_ctl->total_free_space = 0;
@@ -4742,8 +4750,10 @@ static noinline int find_free_extent(struct btrfs_root *root,
 			goto loop;
 		}
 
-		if (!find_free_extent_check_size_class(ffe_ctl, block_group))
+		if (!find_free_extent_check_size_class(ffe_ctl, block_group)) {
+			ffe_ctl->size_class_skipped = true;
 			goto loop;
+		}
 
 		bg_ret = NULL;
 		ret = do_allocation(block_group, ffe_ctl, &bg_ret);
diff --git a/fs/btrfs/extent-tree.h b/fs/btrfs/extent-tree.h
index ff330d4896d6..ecb2a1bf2435 100644
--- a/fs/btrfs/extent-tree.h
+++ b/fs/btrfs/extent-tree.h
@@ -59,6 +59,9 @@ struct find_free_extent_ctl {
 	/* Whether or not the allocator is currently following a hint. */
 	bool hinted;
 
+	/* A block group was skipped for its size class during this loop. */
+	bool size_class_skipped;
+
 	/* RAID index, converted from flags */
 	int index;
 
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/6] btrfs: don't take block group references in find_free_extent()
  2026-09-29  0:28 [PATCH 0/6] btrfs: allocator perf improvements Boris Burkov
  2026-09-29  0:28 ` [PATCH 1/6] btrfs: skip unhelpful size class relaxing allocation loops Boris Burkov
@ 2026-09-29  0:28 ` Boris Burkov
  2026-09-29  0:28 ` [PATCH 3/6] btrfs: remove redundant checks in find_free_extent() hint path Boris Burkov
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Boris Burkov @ 2026-09-29  0:28 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

The get/put pair inside the loop in find_free_extent() is pointless
because the loop is done while holding space_info->groups_sem which is
exclusive with any task deleting the block_group from the raid array,
which also holds a reference. So this can never be the last reference.

Therefore, we can skip the bother of inc/dec-ing the refcount which can
save a considerable amount of time on filesystems with a large number of
full block groups.

A little care must be taken to properly put the refcount for the hint
and bg cluster cases that pick a bg more directly.

Measured on a 30T single-device filesystem on a tmpfs-backed loop device:
  20k sequential 1G fallocates:                     40.1s -> 27.1s
  sync of 20k new 64K files after 20k full 1G bgs:  18.4s -> 13.0s

Assisted-by: LLM
Signed-off-by: Boris Burkov <boris@bur.io>
---
 fs/btrfs/extent-tree.c | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index f43ee0ab24bc..3f4d970cf3ca 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -3777,12 +3777,11 @@ btrfs_lock_block_group(struct btrfs_block_group *cache, bool delalloc)
 		down_read(&cache->data_rwsem);
 }
 
-static inline void btrfs_grab_block_group(struct btrfs_block_group *cache,
-					  bool delalloc)
+static inline void
+btrfs_unlock_block_group(struct btrfs_block_group *cache, bool delalloc)
 {
-	btrfs_get_block_group(cache);
 	if (delalloc)
-		down_read(&cache->data_rwsem);
+		up_read(&cache->data_rwsem);
 }
 
 static struct btrfs_block_group *btrfs_lock_cluster(
@@ -4226,7 +4225,7 @@ static void release_block_group(struct btrfs_block_group *block_group,
 
 	BUG_ON(btrfs_bg_flags_to_raid_index(block_group->flags) !=
 	       ffe_ctl->index);
-	btrfs_release_block_group(block_group, delalloc);
+	btrfs_unlock_block_group(block_group, delalloc);
 }
 
 static void found_extent_clustered(struct find_free_extent_ctl *ffe_ctl,
@@ -4659,6 +4658,7 @@ static noinline int find_free_extent(struct btrfs_root *root,
 			} else {
 				ffe_ctl->index = btrfs_bg_flags_to_raid_index(
 							block_group->flags);
+				btrfs_put_block_group(block_group);
 				btrfs_lock_block_group(block_group,
 						       ffe_ctl->delalloc);
 				ffe_ctl->hinted = true;
@@ -4690,7 +4690,7 @@ static noinline int find_free_extent(struct btrfs_root *root,
 			continue;
 		}
 
-		btrfs_grab_block_group(block_group, ffe_ctl->delalloc);
+		btrfs_lock_block_group(block_group, ffe_ctl->delalloc);
 		ffe_ctl->search_start = block_group->start;
 
 		/*
@@ -4717,7 +4717,7 @@ static noinline int find_free_extent(struct btrfs_root *root,
 			 * It's possible that we have MIXED_GROUP flag but no
 			 * block group is mixed.  Just skip such block group.
 			 */
-			btrfs_release_block_group(block_group, ffe_ctl->delalloc);
+			btrfs_unlock_block_group(block_group, ffe_ctl->delalloc);
 			continue;
 		}
 
@@ -4761,7 +4761,12 @@ static noinline int find_free_extent(struct btrfs_root *root,
 			goto loop;
 
 		if (bg_ret && bg_ret != block_group) {
-			btrfs_release_block_group(block_group, ffe_ctl->delalloc);
+			btrfs_unlock_block_group(block_group, ffe_ctl->delalloc);
+			/*
+			 * bg_ret is still on the list: btrfs_remove_block_group()
+			 * detaches it from the cluster before list_del_init().
+			 */
+			btrfs_put_block_group(bg_ret);
 			block_group = bg_ret;
 		}
 
@@ -4800,7 +4805,7 @@ static noinline int find_free_extent(struct btrfs_root *root,
 		ins->offset = ffe_ctl->num_bytes;
 
 		trace_btrfs_reserve_extent(block_group, ffe_ctl);
-		btrfs_release_block_group(block_group, ffe_ctl->delalloc);
+		btrfs_unlock_block_group(block_group, ffe_ctl->delalloc);
 		break;
 loop:
 		if (!ffe_ctl->cached && ffe_ctl->loop > LOOP_CACHING_NOWAIT &&
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/6] btrfs: remove redundant checks in find_free_extent() hint path
  2026-09-29  0:28 [PATCH 0/6] btrfs: allocator perf improvements Boris Burkov
  2026-09-29  0:28 ` [PATCH 1/6] btrfs: skip unhelpful size class relaxing allocation loops Boris Burkov
  2026-09-29  0:28 ` [PATCH 2/6] btrfs: don't take block group references in find_free_extent() Boris Burkov
@ 2026-09-29  0:28 ` Boris Burkov
  2026-09-29  0:28 ` [PATCH 4/6] btrfs: factor the find_free_extent() block group skip into a helper Boris Burkov
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Boris Burkov @ 2026-09-29  0:28 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

The hint is a long lasting loose reference to a block group which can
outlast the block group or race its destruction. Therefore, using it
from the hint must check that it is still attached to the list of block
groups. However, the other checks are not special to hints and can be
shared with block groups reached normally. So make the hint path check
only the thing that is truly relevant to hints.

Assisted-by: LLM
Signed-off-by: Boris Burkov <boris@bur.io>
---
 fs/btrfs/extent-tree.c | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 3f4d970cf3ca..c4f46d361b0b 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -4644,12 +4644,10 @@ static noinline int find_free_extent(struct btrfs_root *root,
 		    block_group->space_info == space_info &&
 		    block_group->cached != BTRFS_CACHE_NO) {
 			down_read(&space_info->groups_sem);
-			if (list_empty(&block_group->list) ||
-			    block_group->ro ||
-			    (block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED)) {
+			if (list_empty(&block_group->list)) {
 				/*
 				 * someone is removing this block group,
-				 * we can't jump into the have_block_group
+				 * we can't jump into the check_block_group
 				 * target because our list pointers are not
 				 * valid
 				 */
@@ -4659,10 +4657,8 @@ static noinline int find_free_extent(struct btrfs_root *root,
 				ffe_ctl->index = btrfs_bg_flags_to_raid_index(
 							block_group->flags);
 				btrfs_put_block_group(block_group);
-				btrfs_lock_block_group(block_group,
-						       ffe_ctl->delalloc);
 				ffe_ctl->hinted = true;
-				goto have_block_group;
+				goto check_block_group;
 			}
 		} else if (block_group) {
 			btrfs_put_block_group(block_group);
@@ -4680,6 +4676,8 @@ static noinline int find_free_extent(struct btrfs_root *root,
 		struct btrfs_block_group *bg_ret;
 
 		ffe_ctl->hinted = false;
+		ffe_ctl->search_start = block_group->start;
+check_block_group:
 		/* If the block group is read-only, we can skip it entirely. */
 		if (unlikely(block_group->ro ||
 			     (block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED))) {
@@ -4691,7 +4689,6 @@ static noinline int find_free_extent(struct btrfs_root *root,
 		}
 
 		btrfs_lock_block_group(block_group, ffe_ctl->delalloc);
-		ffe_ctl->search_start = block_group->start;
 
 		/*
 		 * this can happen if we end up cycling through all the
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 4/6] btrfs: factor the find_free_extent() block group skip into a helper
  2026-09-29  0:28 [PATCH 0/6] btrfs: allocator perf improvements Boris Burkov
                   ` (2 preceding siblings ...)
  2026-09-29  0:28 ` [PATCH 3/6] btrfs: remove redundant checks in find_free_extent() hint path Boris Burkov
@ 2026-09-29  0:28 ` Boris Burkov
  2026-09-29  0:28 ` [PATCH 5/6] btrfs: find_free_extent checks full block groups unlocked Boris Burkov
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Boris Burkov @ 2026-09-29  0:28 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

find_free_extent() is already quite unwieldy so I believe a well defined
"usability check" is a helpful thing to pull out into a named static
function just to break things down into easier to grok components.

No functional change.

Signed-off-by: Boris Burkov <boris@bur.io>
---
 fs/btrfs/extent-tree.c | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index c4f46d361b0b..01cab703ef3b 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -4321,6 +4321,18 @@ static int can_allocate_chunk(struct btrfs_fs_info *fs_info,
 	}
 }
 
+static bool find_free_extent_skip_block_group(struct btrfs_block_group *block_group,
+					      struct find_free_extent_ctl *ffe_ctl)
+{
+	if (likely(!block_group->ro && !(block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED)))
+		return false;
+	if (ffe_ctl->for_treelog)
+		btrfs_clear_treelog_bg(block_group);
+	if (ffe_ctl->for_data_reloc)
+		btrfs_clear_data_reloc_bg(block_group);
+	return true;
+}
+
 /*
  * Return >0 means caller needs to re-search for free extent
  * Return 0 means we have the needed free extent.
@@ -4678,15 +4690,8 @@ static noinline int find_free_extent(struct btrfs_root *root,
 		ffe_ctl->hinted = false;
 		ffe_ctl->search_start = block_group->start;
 check_block_group:
-		/* If the block group is read-only, we can skip it entirely. */
-		if (unlikely(block_group->ro ||
-			     (block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED))) {
-			if (ffe_ctl->for_treelog)
-				btrfs_clear_treelog_bg(block_group);
-			if (ffe_ctl->for_data_reloc)
-				btrfs_clear_data_reloc_bg(block_group);
+		if (find_free_extent_skip_block_group(block_group, ffe_ctl))
 			continue;
-		}
 
 		btrfs_lock_block_group(block_group, ffe_ctl->delalloc);
 
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 5/6] btrfs: find_free_extent checks full block groups unlocked
  2026-09-29  0:28 [PATCH 0/6] btrfs: allocator perf improvements Boris Burkov
                   ` (3 preceding siblings ...)
  2026-09-29  0:28 ` [PATCH 4/6] btrfs: factor the find_free_extent() block group skip into a helper Boris Burkov
@ 2026-09-29  0:28 ` Boris Burkov
  2026-09-29  0:28 ` [PATCH 6/6] btrfs: find_free_extent checks size class unlocked Boris Burkov
  2026-09-29  4:31 ` [PATCH 0/6] btrfs: allocator perf improvements Sun YangKai
  6 siblings, 0 replies; 10+ messages in thread
From: Boris Burkov @ 2026-09-29  0:28 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

No matter how you slice it, a block group with less free space than the
allocation requires cannot satisfy the allocation, so we can skip it. We
can do this check without taking a lock because it saying yes does *not*
mean that we definitely do find the space locked properly later when
doing the real allocation. But racing with a free and skipping here,
even if we might have said yes could still have raced the same way with
the lock and the allocation still would have failed. On top of that,
this has many fallbacks like the multiple find_free_extent loops and the
fact that on failure we reduce the allocation size and try again.

This helps improve performance on a common case where we have many full
block groups in a large filesystem and want to rapidly skip them.

Measured on a 30T single-device filesystem on a tmpfs-backed loop device:
  20k sequential 1G fallocates:                           26.9s -> 6.7s
  sync of 20k new 64K files after 20k full 1G bgs:        12.7s -> 3.7s
  sync of 20k new 64K files after 20k 1000M fallocates:   12.6s -> 13.2s

Assisted-by: LLM
Signed-off-by: Boris Burkov <boris@bur.io>
---
 fs/btrfs/extent-tree.c | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 01cab703ef3b..d96de10348c1 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -4324,7 +4324,23 @@ static int can_allocate_chunk(struct btrfs_fs_info *fs_info,
 static bool find_free_extent_skip_block_group(struct btrfs_block_group *block_group,
 					      struct find_free_extent_ctl *ffe_ctl)
 {
-	if (likely(!block_group->ro && !(block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED)))
+	bool skip = block_group->ro || (block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED);
+
+	/*
+	 * caching_thread() sets FINISHED under a a separate block_group->lock
+	 * critical section after it is done populating, so smp_load_acquire
+	 * guarantees we see the block group as populated by caching.
+	 */
+	if (!skip && smp_load_acquire(&block_group->cached) == BTRFS_CACHE_FINISHED) {
+		u64 free_space = data_race(block_group->free_space_ctl->free_space);
+
+		if (free_space < ffe_ctl->num_bytes) {
+			ffe_ctl->total_free_space = max(ffe_ctl->total_free_space,
+							free_space);
+			skip = true;
+		}
+	}
+	if (!skip)
 		return false;
 	if (ffe_ctl->for_treelog)
 		btrfs_clear_treelog_bg(block_group);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 6/6] btrfs: find_free_extent checks size class unlocked
  2026-09-29  0:28 [PATCH 0/6] btrfs: allocator perf improvements Boris Burkov
                   ` (4 preceding siblings ...)
  2026-09-29  0:28 ` [PATCH 5/6] btrfs: find_free_extent checks full block groups unlocked Boris Burkov
@ 2026-09-29  0:28 ` Boris Burkov
  2026-09-29  4:31 ` [PATCH 0/6] btrfs: allocator perf improvements Sun YangKai
  6 siblings, 0 replies; 10+ messages in thread
From: Boris Burkov @ 2026-09-29  0:28 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

Similar to the free space check, it doesn't hurt to skip a block group
for having the wrong size class even if it maybe was just about to
get reset to no size class. Furthermore, setting the size class already
admits that races are possible and that there is no guarantee of
perfection of matching size classes, just that we only set it once per
bg per reset.

This can also help quickly iterate past thousands of wrong size class
bgs in a large fs without taking bg locks on each pass.

Measured on a 30T single-device filesystem on a tmpfs-backed loop device:
  20k sequential 1G fallocates:                            6.7s -> 7.0s
  sync of 20k new 64K files after 20k full 1G bgs:         3.7s -> 3.8s
  sync of 20k new 64K files after 20k 1000M fallocates:   13.2s -> 4.8s

Assisted-by: LLM
Signed-off-by: Boris Burkov <boris@bur.io>
---
 fs/btrfs/extent-tree.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index d96de10348c1..10beb78a5ba3 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -4338,6 +4338,9 @@ static bool find_free_extent_skip_block_group(struct btrfs_block_group *block_gr
 			ffe_ctl->total_free_space = max(ffe_ctl->total_free_space,
 							free_space);
 			skip = true;
+		} else if (!find_free_extent_check_size_class(ffe_ctl, block_group)) {
+			ffe_ctl->size_class_skipped = true;
+			skip = true;
 		}
 	}
 	if (!skip)
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/6] btrfs: allocator perf improvements
  2026-09-29  0:28 [PATCH 0/6] btrfs: allocator perf improvements Boris Burkov
                   ` (5 preceding siblings ...)
  2026-09-29  0:28 ` [PATCH 6/6] btrfs: find_free_extent checks size class unlocked Boris Burkov
@ 2026-09-29  4:31 ` Sun YangKai
  2026-09-29 17:04   ` Boris Burkov
  6 siblings, 1 reply; 10+ messages in thread
From: Sun YangKai @ 2026-09-29  4:31 UTC (permalink / raw)
  To: Boris Burkov, linux-btrfs, kernel-team

On 2026/9/29 08:28, Boris Burkov wrote:
> Working on the bugs to do with large fs-es and unused block groups
> 
> https://lore.kernel.org/linux-btrfs/20260916230447.GA1706558@zen.localdomain/T/#t
> and
> https://lore.kernel.org/linux-btrfs/cover.1790641111.git.boris@bur.io/T/#t
> 
> revealed some low hanging fruit in allocation performance for very large
> mostly full fs-es. The gist is to try to skip unnecessary operations per block
> group which adds up with 10s of thousands of obviously skippable block
> groups. Running 20k fallocates in a loop on a sparse file fs improved from
> ~40s to ~7s.

Hi Boris,

Glad to see this patchset resolves the long existing blockgroup 
allocator performance issue and I'm really impressed by the numbers of 
performance improvement.

IMO we currently go along the list to look for a proper bg that have 
enough free space, which means we may have to go through thousands of 
bgs during the allocation, I wonder if it is worthy to use a more 
efficient method like something sorted by bg's free space to improve the 
lookup process?

Please correct me if I got anything wrong.

And thanks for bringing these great patches.

Sun Yangkai

> Boris Burkov (6):
>    btrfs: skip unhelpful size class relaxing allocation loops
>    btrfs: don't take block group references in find_free_extent()
>    btrfs: remove redundant checks in find_free_extent() hint path
>    btrfs: factor the find_free_extent() block group skip into a helper
>    btrfs: find_free_extent checks full block groups unlocked
>    btrfs: find_free_extent checks size class unlocked
> 
>   fs/btrfs/extent-tree.c | 88 +++++++++++++++++++++++++++++-------------
>   fs/btrfs/extent-tree.h |  3 ++
>   2 files changed, 65 insertions(+), 26 deletions(-)

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/6] btrfs: allocator perf improvements
  2026-09-29  4:31 ` [PATCH 0/6] btrfs: allocator perf improvements Sun YangKai
@ 2026-09-29 17:04   ` Boris Burkov
  2026-09-29 19:07     ` Martin Raiber
  0 siblings, 1 reply; 10+ messages in thread
From: Boris Burkov @ 2026-09-29 17:04 UTC (permalink / raw)
  To: Sun YangKai; +Cc: linux-btrfs, kernel-team

On Tue, Sep 29, 2026 at 12:31:16PM +0800, Sun YangKai wrote:
> On 2026/9/29 08:28, Boris Burkov wrote:
> > Working on the bugs to do with large fs-es and unused block groups
> > 
> > https://lore.kernel.org/linux-btrfs/20260916230447.GA1706558@zen.localdomain/T/#t
> > and
> > https://lore.kernel.org/linux-btrfs/cover.1790641111.git.boris@bur.io/T/#t
> > 
> > revealed some low hanging fruit in allocation performance for very large
> > mostly full fs-es. The gist is to try to skip unnecessary operations per block
> > group which adds up with 10s of thousands of obviously skippable block
> > groups. Running 20k fallocates in a loop on a sparse file fs improved from
> > ~40s to ~7s.
> 
> Hi Boris,
> 
> Glad to see this patchset resolves the long existing blockgroup 
> allocator performance issue and I'm really impressed by the numbers of 
> performance improvement.
> 
> IMO we currently go along the list to look for a proper bg that have 
> enough free space, which means we may have to go through thousands of 
> bgs during the allocation, I wonder if it is worthy to use a more 
> efficient method like something sorted by bg's free space to improve the 
> lookup process?

I considered this while working on the size class stuff some years ago.
If you look into the free space cache inside each block group, it does
in fact index by gap size as well as gap offset.

However if you do that at the block group level, then each allocation
requires a write to the global ordering of block groups so that means
having many parallel allocating threads now requires them to fight over
a write lock on that ordered structure. I think that at least rules out
naive solutions.

You could probably resolve it with some more clever scheme like batching
or queuing the work to update the ordering, or maybe some duplicated
view with atomic cut overs.

If you are interested in allocator performance, another thing we should
consider is resurrecting more of
https://lore.kernel.org/linux-btrfs/0102019bb2ff5805-8aa151b8-1fe7-4087-9610-4c3314b3b144-000000@eu-west-1.amazonses.com/

I actually didn't realize that Martin had hit a few of the same things
in that series so now I feel a bit bad about not working harder to
incorporate his work earlier.

If this series of mine goes in, we are ok on the put/get part since I
just got rid of it, and I was planning to get rid of data_rwsem after
https://lore.kernel.org/linux-btrfs/20260916-btrfs-remove-v1-space-cache-v3-0-10ebde03b96f@columbia.edu/
goes in. But I have not tried to tackle the scalability of groups_sem
yet, and would probably start by seriously evaluating Martin's patch and
re-sending his work for that one.

> 
> Please correct me if I got anything wrong.
> 
> And thanks for bringing these great patches.
> 
> Sun Yangkai
> 
> > Boris Burkov (6):
> >    btrfs: skip unhelpful size class relaxing allocation loops
> >    btrfs: don't take block group references in find_free_extent()
> >    btrfs: remove redundant checks in find_free_extent() hint path
> >    btrfs: factor the find_free_extent() block group skip into a helper
> >    btrfs: find_free_extent checks full block groups unlocked
> >    btrfs: find_free_extent checks size class unlocked
> > 
> >   fs/btrfs/extent-tree.c | 88 +++++++++++++++++++++++++++++-------------
> >   fs/btrfs/extent-tree.h |  3 ++
> >   2 files changed, 65 insertions(+), 26 deletions(-)

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 0/6] btrfs: allocator perf improvements
  2026-09-29 17:04   ` Boris Burkov
@ 2026-09-29 19:07     ` Martin Raiber
  0 siblings, 0 replies; 10+ messages in thread
From: Martin Raiber @ 2026-09-29 19:07 UTC (permalink / raw)
  To: Boris Burkov, Sun YangKai; +Cc: linux-btrfs, kernel-team

On 9/29/26 19:04 Boris Burkov wrote:
> On Tue, Sep 29, 2026 at 12:31:16PM +0800, Sun YangKai wrote:
>> On 2026/9/29 08:28, Boris Burkov wrote:
>>> Working on the bugs to do with large fs-es and unused block groups
>>>
>>> https://lore.kernel.org/linux-btrfs/20260916230447.GA1706558@zen.localdomain/T/#t
>>> and
>>> https://lore.kernel.org/linux-btrfs/cover.1790641111.git.boris@bur.io/T/#t
>>>
>>> revealed some low hanging fruit in allocation performance for very large
>>> mostly full fs-es. The gist is to try to skip unnecessary operations per block
>>> group which adds up with 10s of thousands of obviously skippable block
>>> groups. Running 20k fallocates in a loop on a sparse file fs improved from
>>> ~40s to ~7s.
>> Hi Boris,
>>
>> Glad to see this patchset resolves the long existing blockgroup 
>> allocator performance issue and I'm really impressed by the numbers of 
>> performance improvement.
>>
>> IMO we currently go along the list to look for a proper bg that have 
>> enough free space, which means we may have to go through thousands of 
>> bgs during the allocation, I wonder if it is worthy to use a more 
>> efficient method like something sorted by bg's free space to improve the 
>> lookup process?
> I considered this while working on the size class stuff some years ago.
> If you look into the free space cache inside each block group, it does
> in fact index by gap size as well as gap offset.
>
> However if you do that at the block group level, then each allocation
> requires a write to the global ordering of block groups so that means
> having many parallel allocating threads now requires them to fight over
> a write lock on that ordered structure. I think that at least rules out
> naive solutions.
>
> You could probably resolve it with some more clever scheme like batching
> or queuing the work to update the ordering, or maybe some duplicated
> view with atomic cut overs.
>
> If you are interested in allocator performance, another thing we should
> consider is resurrecting more of
> https://lore.kernel.org/linux-btrfs/0102019bb2ff5805-8aa151b8-1fe7-4087-9610-4c3314b3b144-000000@eu-west-1.amazonses.com/
>
> I actually didn't realize that Martin had hit a few of the same things
> in that series so now I feel a bit bad about not working harder to
> incorporate his work earlier.
>
> If this series of mine goes in, we are ok on the put/get part since I
> just got rid of it, and I was planning to get rid of data_rwsem after
> https://lore.kernel.org/linux-btrfs/20260916-btrfs-remove-v1-space-cache-v3-0-10ebde03b96f@columbia.edu/
> goes in. But I have not tried to tackle the scalability of groups_sem
> yet, and would probably start by seriously evaluating Martin's patch and
> re-sending his work for that one.

Np, I was just solving for the specific problems I saw with minimum effort.

I would imagine there might be performance problems with just replacing with a percpu semaphore (e.g. with zoned).
Maybe it needs to be a RCU list? Or different arenas like memory allocators (since you are already using size classes...)?

>
>> Please correct me if I got anything wrong.
>>
>> And thanks for bringing these great patches.
>>
>> Sun Yangkai
>>
>>> Boris Burkov (6):
>>>    btrfs: skip unhelpful size class relaxing allocation loops
>>>    btrfs: don't take block group references in find_free_extent()
>>>    btrfs: remove redundant checks in find_free_extent() hint path
>>>    btrfs: factor the find_free_extent() block group skip into a helper
>>>    btrfs: find_free_extent checks full block groups unlocked
>>>    btrfs: find_free_extent checks size class unlocked
>>>
>>>   fs/btrfs/extent-tree.c | 88 +++++++++++++++++++++++++++++-------------
>>>   fs/btrfs/extent-tree.h |  3 ++
>>>   2 files changed, 65 insertions(+), 26 deletions(-)



^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-09-29 19:07 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-29  0:28 [PATCH 0/6] btrfs: allocator perf improvements Boris Burkov
2026-09-29  0:28 ` [PATCH 1/6] btrfs: skip unhelpful size class relaxing allocation loops Boris Burkov
2026-09-29  0:28 ` [PATCH 2/6] btrfs: don't take block group references in find_free_extent() Boris Burkov
2026-09-29  0:28 ` [PATCH 3/6] btrfs: remove redundant checks in find_free_extent() hint path Boris Burkov
2026-09-29  0:28 ` [PATCH 4/6] btrfs: factor the find_free_extent() block group skip into a helper Boris Burkov
2026-09-29  0:28 ` [PATCH 5/6] btrfs: find_free_extent checks full block groups unlocked Boris Burkov
2026-09-29  0:28 ` [PATCH 6/6] btrfs: find_free_extent checks size class unlocked Boris Burkov
2026-09-29  4:31 ` [PATCH 0/6] btrfs: allocator perf improvements Sun YangKai
2026-09-29 17:04   ` Boris Burkov
2026-09-29 19:07     ` Martin Raiber

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.