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: Qu Wenruo <wqu@suse.com> Reviewed-by: Su Yue <suy.fnst@cn.fujitsu.com> Reviewed-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
@@ -7305,6 +7305,116 @@ struct find_free_extent_ctl {
|
|||||||
u64 found_offset;
|
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.
|
* walks the btree of allocated extents and find a hole of a given size.
|
||||||
* The key ins is changed to record the hole:
|
* The key ins is changed to record the hole:
|
||||||
@@ -7487,137 +7597,26 @@ have_block_group:
|
|||||||
* lets look there
|
* lets look there
|
||||||
*/
|
*/
|
||||||
if (last_ptr && use_cluster) {
|
if (last_ptr && use_cluster) {
|
||||||
struct btrfs_block_group_cache *used_block_group;
|
struct btrfs_block_group_cache *cluster_bg = NULL;
|
||||||
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 &&
|
ret = find_free_extent_clustered(block_group, last_ptr,
|
||||||
(used_block_group->ro ||
|
&ffe_ctl, &cluster_bg);
|
||||||
!block_group_bits(used_block_group,
|
|
||||||
ffe_ctl.flags)))
|
|
||||||
goto release_cluster;
|
|
||||||
|
|
||||||
ffe_ctl.found_offset = btrfs_alloc_from_cluster(
|
if (ret == 0) {
|
||||||
used_block_group,
|
if (cluster_bg && cluster_bg != 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,
|
btrfs_release_block_group(block_group,
|
||||||
delalloc);
|
delalloc);
|
||||||
block_group = used_block_group;
|
block_group = cluster_bg;
|
||||||
}
|
}
|
||||||
goto checks;
|
goto checks;
|
||||||
}
|
} else if (ret == -EAGAIN) {
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* this cluster didn't work out, free it and
|
|
||||||
* start over
|
|
||||||
*/
|
|
||||||
btrfs_return_cluster_to_free_space(NULL, last_ptr);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
} 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 have_block_group;
|
goto have_block_group;
|
||||||
}
|
} else if (ret > 0) {
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
goto loop;
|
||||||
}
|
}
|
||||||
|
/* ret == -ENOENT case falls through */
|
||||||
|
}
|
||||||
|
|
||||||
unclustered_alloc:
|
|
||||||
/*
|
/*
|
||||||
* We are doing an unclustered alloc, set the fragmented flag so
|
* We are doing an unclustered alloc, set the fragmented flag so
|
||||||
* we don't bother trying to setup a cluster again until we get
|
* we don't bother trying to setup a cluster again until we get
|
||||||
|
Reference in New Issue
Block a user