Commit d06e3bb6 authored by Qu Wenruo's avatar Qu Wenruo Committed by David Sterba

btrfs: Refactor clustered extent allocation into find_free_extent_clustered

We have two main methods to find free extents inside a block group:

1) clustered allocation
2) unclustered allocation

This patch will extract the clustered allocation into
find_free_extent_clustered() to make it a little easier to read.

Instead of jumping between different labels in find_free_extent(), the
helper function will use return value to indicate different behavior.
Signed-off-by: default avatarQu Wenruo <wqu@suse.com>
Reviewed-by: default avatarSu Yue <suy.fnst@cn.fujitsu.com>
Reviewed-by: default avatarJosef Bacik <josef@toxicpanda.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent b4bd745d
......@@ -7305,6 +7305,116 @@ struct find_free_extent_ctl {
u64 found_offset;
};
/*
* Helper function for find_free_extent().
*
* Return -ENOENT to inform caller that we need fallback to unclustered mode.
* Return -EAGAIN to inform caller that we need to re-search this block group
* Return >0 to inform caller that we find nothing
* Return 0 means we have found a location and set ffe_ctl->found_offset.
*/
static int find_free_extent_clustered(struct btrfs_block_group_cache *bg,
struct btrfs_free_cluster *last_ptr,
struct find_free_extent_ctl *ffe_ctl,
struct btrfs_block_group_cache **cluster_bg_ret)
{
struct btrfs_fs_info *fs_info = bg->fs_info;
struct btrfs_block_group_cache *cluster_bg;
u64 aligned_cluster;
u64 offset;
int ret;
cluster_bg = btrfs_lock_cluster(bg, last_ptr, ffe_ctl->delalloc);
if (!cluster_bg)
goto refill_cluster;
if (cluster_bg != bg && (cluster_bg->ro ||
!block_group_bits(cluster_bg, ffe_ctl->flags)))
goto release_cluster;
offset = btrfs_alloc_from_cluster(cluster_bg, last_ptr,
ffe_ctl->num_bytes, cluster_bg->key.objectid,
&ffe_ctl->max_extent_size);
if (offset) {
/* We have a block, we're done */
spin_unlock(&last_ptr->refill_lock);
trace_btrfs_reserve_extent_cluster(cluster_bg,
ffe_ctl->search_start, ffe_ctl->num_bytes);
*cluster_bg_ret = cluster_bg;
ffe_ctl->found_offset = offset;
return 0;
}
WARN_ON(last_ptr->block_group != cluster_bg);
release_cluster:
/*
* If we are on LOOP_NO_EMPTY_SIZE, we can't set up a new clusters, so
* lets just skip it and let the allocator find whatever block it can
* find. If we reach this point, we will have tried the cluster
* allocator plenty of times and not have found anything, so we are
* likely way too fragmented for the clustering stuff to find anything.
*
* However, if the cluster is taken from the current block group,
* release the cluster first, so that we stand a better chance of
* succeeding in the unclustered allocation.
*/
if (ffe_ctl->loop >= LOOP_NO_EMPTY_SIZE && cluster_bg != bg) {
spin_unlock(&last_ptr->refill_lock);
btrfs_release_block_group(cluster_bg, ffe_ctl->delalloc);
return -ENOENT;
}
/* This cluster didn't work out, free it and start over */
btrfs_return_cluster_to_free_space(NULL, last_ptr);
if (cluster_bg != bg)
btrfs_release_block_group(cluster_bg, ffe_ctl->delalloc);
refill_cluster:
if (ffe_ctl->loop >= LOOP_NO_EMPTY_SIZE) {
spin_unlock(&last_ptr->refill_lock);
return -ENOENT;
}
aligned_cluster = max_t(u64,
ffe_ctl->empty_cluster + ffe_ctl->empty_size,
bg->full_stripe_len);
ret = btrfs_find_space_cluster(fs_info, bg, last_ptr,
ffe_ctl->search_start, ffe_ctl->num_bytes,
aligned_cluster);
if (ret == 0) {
/* Now pull our allocation out of this cluster */
offset = btrfs_alloc_from_cluster(bg, last_ptr,
ffe_ctl->num_bytes, ffe_ctl->search_start,
&ffe_ctl->max_extent_size);
if (offset) {
/* We found one, proceed */
spin_unlock(&last_ptr->refill_lock);
trace_btrfs_reserve_extent_cluster(bg,
ffe_ctl->search_start,
ffe_ctl->num_bytes);
ffe_ctl->found_offset = offset;
return 0;
}
} else if (!ffe_ctl->cached && ffe_ctl->loop > LOOP_CACHING_NOWAIT &&
!ffe_ctl->retry_clustered) {
spin_unlock(&last_ptr->refill_lock);
ffe_ctl->retry_clustered = true;
wait_block_group_cache_progress(bg, ffe_ctl->num_bytes +
ffe_ctl->empty_cluster + ffe_ctl->empty_size);
return -EAGAIN;
}
/*
* At this point we either didn't find a cluster or we weren't able to
* allocate a block from our cluster. Free the cluster we've been
* trying to use, and go to the next block group.
*/
btrfs_return_cluster_to_free_space(NULL, last_ptr);
spin_unlock(&last_ptr->refill_lock);
return 1;
}
/*
* walks the btree of allocated extents and find a hole of a given size.
* The key ins is changed to record the hole:
......@@ -7487,137 +7597,26 @@ static noinline int find_free_extent(struct btrfs_fs_info *fs_info,
* lets look there
*/
if (last_ptr && use_cluster) {
struct btrfs_block_group_cache *used_block_group;
unsigned long aligned_cluster;
/*
* the refill lock keeps out other
* people trying to start a new cluster
*/
used_block_group = btrfs_lock_cluster(block_group,
last_ptr,
delalloc);
if (!used_block_group)
goto refill_cluster;
if (used_block_group != block_group &&
(used_block_group->ro ||
!block_group_bits(used_block_group,
ffe_ctl.flags)))
goto release_cluster;
ffe_ctl.found_offset = btrfs_alloc_from_cluster(
used_block_group,
last_ptr,
num_bytes,
used_block_group->key.objectid,
&ffe_ctl.max_extent_size);
if (ffe_ctl.found_offset) {
/* we have a block, we're done */
spin_unlock(&last_ptr->refill_lock);
trace_btrfs_reserve_extent_cluster(
used_block_group,
ffe_ctl.search_start,
num_bytes);
if (used_block_group != block_group) {
btrfs_release_block_group(block_group,
delalloc);
block_group = used_block_group;
}
goto checks;
}
WARN_ON(last_ptr->block_group != used_block_group);
release_cluster:
/* If we are on LOOP_NO_EMPTY_SIZE, we can't
* set up a new clusters, so lets just skip it
* and let the allocator find whatever block
* it can find. If we reach this point, we
* will have tried the cluster allocator
* plenty of times and not have found
* anything, so we are likely way too
* fragmented for the clustering stuff to find
* anything.
*
* However, if the cluster is taken from the
* current block group, release the cluster
* first, so that we stand a better chance of
* succeeding in the unclustered
* allocation. */
if (ffe_ctl.loop >= LOOP_NO_EMPTY_SIZE &&
used_block_group != block_group) {
spin_unlock(&last_ptr->refill_lock);
btrfs_release_block_group(used_block_group,
delalloc);
goto unclustered_alloc;
}
struct btrfs_block_group_cache *cluster_bg = NULL;
/*
* this cluster didn't work out, free it and
* start over
*/
btrfs_return_cluster_to_free_space(NULL, last_ptr);
ret = find_free_extent_clustered(block_group, last_ptr,
&ffe_ctl, &cluster_bg);
if (used_block_group != block_group)
btrfs_release_block_group(used_block_group,
delalloc);
refill_cluster:
if (ffe_ctl.loop >= LOOP_NO_EMPTY_SIZE) {
spin_unlock(&last_ptr->refill_lock);
goto unclustered_alloc;
}
aligned_cluster = max_t(unsigned long,
ffe_ctl.empty_cluster + empty_size,
block_group->full_stripe_len);
/* allocate a cluster in this block group */
ret = btrfs_find_space_cluster(fs_info, block_group,
last_ptr,
ffe_ctl.search_start,
num_bytes,
aligned_cluster);
if (ret == 0) {
/*
* now pull our allocation out of this
* cluster
*/
ffe_ctl.found_offset = btrfs_alloc_from_cluster(
block_group, last_ptr,
num_bytes, ffe_ctl.search_start,
&ffe_ctl.max_extent_size);
if (ffe_ctl.found_offset) {
/* we found one, proceed */
spin_unlock(&last_ptr->refill_lock);
trace_btrfs_reserve_extent_cluster(
block_group,
ffe_ctl.search_start,
num_bytes);
goto checks;
if (cluster_bg && cluster_bg != block_group) {
btrfs_release_block_group(block_group,
delalloc);
block_group = cluster_bg;
}
} else if (!ffe_ctl.cached &&
ffe_ctl.loop > LOOP_CACHING_NOWAIT &&
!ffe_ctl.retry_clustered) {
spin_unlock(&last_ptr->refill_lock);
ffe_ctl.retry_clustered = true;
wait_block_group_cache_progress(block_group,
num_bytes + ffe_ctl.empty_cluster +
empty_size);
goto checks;
} else if (ret == -EAGAIN) {
goto have_block_group;
} else if (ret > 0) {
goto loop;
}
/*
* at this point we either didn't find a cluster
* or we weren't able to allocate a block from our
* cluster. Free the cluster we've been trying
* to use, and go to the next block group
*/
btrfs_return_cluster_to_free_space(NULL, last_ptr);
spin_unlock(&last_ptr->refill_lock);
goto loop;
/* ret == -ENOENT case falls through */
}
unclustered_alloc:
/*
* We are doing an unclustered alloc, set the fragmented flag so
* we don't bother trying to setup a cluster again until we get
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment