Commit 274bd4fb authored by Alexandre Oliva's avatar Alexandre Oliva Committed by Chris Mason

Btrfs: try cluster but don't advance in search list

When we find an existing cluster, we switch to its block group as the
current block group, possibly skipping multiple blocks in the process.
Furthermore, under heavy contention, multiple threads may fail to
allocate from a cluster and then release just-created clusters just to
proceed to create new ones in a different block group.

This patch tries to allocate from an existing cluster regardless of its
block group, and doesn't switch to that group, instead proceeding to
try to allocate a cluster from the group it was iterating before the
attempt.
Signed-off-by: default avatarAlexandre Oliva <oliva@lsd.ic.unicamp.br>
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 062c05c4
...@@ -5106,11 +5106,11 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans, ...@@ -5106,11 +5106,11 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root = orig_root->fs_info->extent_root; struct btrfs_root *root = orig_root->fs_info->extent_root;
struct btrfs_free_cluster *last_ptr = NULL; struct btrfs_free_cluster *last_ptr = NULL;
struct btrfs_block_group_cache *block_group = NULL; struct btrfs_block_group_cache *block_group = NULL;
struct btrfs_block_group_cache *used_block_group;
int empty_cluster = 2 * 1024 * 1024; int empty_cluster = 2 * 1024 * 1024;
int allowed_chunk_alloc = 0; int allowed_chunk_alloc = 0;
int done_chunk_alloc = 0; int done_chunk_alloc = 0;
struct btrfs_space_info *space_info; struct btrfs_space_info *space_info;
int last_ptr_loop = 0;
int loop = 0; int loop = 0;
int index = 0; int index = 0;
int alloc_type = (data & BTRFS_BLOCK_GROUP_DATA) ? int alloc_type = (data & BTRFS_BLOCK_GROUP_DATA) ?
...@@ -5172,6 +5172,7 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans, ...@@ -5172,6 +5172,7 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans,
ideal_cache: ideal_cache:
block_group = btrfs_lookup_block_group(root->fs_info, block_group = btrfs_lookup_block_group(root->fs_info,
search_start); search_start);
used_block_group = block_group;
/* /*
* we don't want to use the block group if it doesn't match our * we don't want to use the block group if it doesn't match our
* allocation bits, or if its not cached. * allocation bits, or if its not cached.
...@@ -5209,6 +5210,7 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans, ...@@ -5209,6 +5210,7 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans,
u64 offset; u64 offset;
int cached; int cached;
used_block_group = block_group;
btrfs_get_block_group(block_group); btrfs_get_block_group(block_group);
search_start = block_group->key.objectid; search_start = block_group->key.objectid;
...@@ -5294,49 +5296,33 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans, ...@@ -5294,49 +5296,33 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans,
* people trying to start a new cluster * people trying to start a new cluster
*/ */
spin_lock(&last_ptr->refill_lock); spin_lock(&last_ptr->refill_lock);
if (!last_ptr->block_group || used_block_group = last_ptr->block_group;
last_ptr->block_group->ro || if (used_block_group != block_group &&
!block_group_bits(last_ptr->block_group, data)) (!used_block_group ||
used_block_group->ro ||
!block_group_bits(used_block_group, data))) {
used_block_group = block_group;
goto refill_cluster; goto refill_cluster;
}
if (used_block_group != block_group)
btrfs_get_block_group(used_block_group);
offset = btrfs_alloc_from_cluster(block_group, last_ptr, offset = btrfs_alloc_from_cluster(used_block_group,
num_bytes, search_start); last_ptr, num_bytes, used_block_group->key.objectid);
if (offset) { if (offset) {
/* we have a block, we're done */ /* we have a block, we're done */
spin_unlock(&last_ptr->refill_lock); spin_unlock(&last_ptr->refill_lock);
goto checks; goto checks;
} }
spin_lock(&last_ptr->lock); WARN_ON(last_ptr->block_group != used_block_group);
/* if (used_block_group != block_group) {
* whoops, this cluster doesn't actually point to btrfs_put_block_group(used_block_group);
* this block group. Get a ref on the block used_block_group = block_group;
* group is does point to and try again
*/
if (!last_ptr_loop && last_ptr->block_group &&
last_ptr->block_group != block_group &&
index <=
get_block_group_index(last_ptr->block_group)) {
btrfs_put_block_group(block_group);
block_group = last_ptr->block_group;
btrfs_get_block_group(block_group);
spin_unlock(&last_ptr->lock);
spin_unlock(&last_ptr->refill_lock);
last_ptr_loop = 1;
search_start = block_group->key.objectid;
/*
* we know this block group is properly
* in the list because
* btrfs_remove_block_group, drops the
* cluster before it removes the block
* group from the list
*/
goto have_block_group;
} }
spin_unlock(&last_ptr->lock);
refill_cluster: refill_cluster:
BUG_ON(used_block_group != block_group);
/* If we are on LOOP_NO_EMPTY_SIZE, we can't /* If we are on LOOP_NO_EMPTY_SIZE, we can't
* set up a new clusters, so lets just skip it * set up a new clusters, so lets just skip it
* and let the allocator find whatever block * and let the allocator find whatever block
...@@ -5357,8 +5343,6 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans, ...@@ -5357,8 +5343,6 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans,
*/ */
btrfs_return_cluster_to_free_space(NULL, last_ptr); btrfs_return_cluster_to_free_space(NULL, last_ptr);
last_ptr_loop = 0;
/* allocate a cluster in this block group */ /* allocate a cluster in this block group */
ret = btrfs_find_space_cluster(trans, root, ret = btrfs_find_space_cluster(trans, root,
block_group, last_ptr, block_group, last_ptr,
...@@ -5425,14 +5409,14 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans, ...@@ -5425,14 +5409,14 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans,
search_start = stripe_align(root, offset); search_start = stripe_align(root, offset);
/* move on to the next group */ /* move on to the next group */
if (search_start + num_bytes >= search_end) { if (search_start + num_bytes >= search_end) {
btrfs_add_free_space(block_group, offset, num_bytes); btrfs_add_free_space(used_block_group, offset, num_bytes);
goto loop; goto loop;
} }
/* move on to the next group */ /* move on to the next group */
if (search_start + num_bytes > if (search_start + num_bytes >
block_group->key.objectid + block_group->key.offset) { used_block_group->key.objectid + used_block_group->key.offset) {
btrfs_add_free_space(block_group, offset, num_bytes); btrfs_add_free_space(used_block_group, offset, num_bytes);
goto loop; goto loop;
} }
...@@ -5440,14 +5424,14 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans, ...@@ -5440,14 +5424,14 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans,
ins->offset = num_bytes; ins->offset = num_bytes;
if (offset < search_start) if (offset < search_start)
btrfs_add_free_space(block_group, offset, btrfs_add_free_space(used_block_group, offset,
search_start - offset); search_start - offset);
BUG_ON(offset > search_start); BUG_ON(offset > search_start);
ret = btrfs_update_reserved_bytes(block_group, num_bytes, ret = btrfs_update_reserved_bytes(used_block_group, num_bytes,
alloc_type); alloc_type);
if (ret == -EAGAIN) { if (ret == -EAGAIN) {
btrfs_add_free_space(block_group, offset, num_bytes); btrfs_add_free_space(used_block_group, offset, num_bytes);
goto loop; goto loop;
} }
...@@ -5456,15 +5440,19 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans, ...@@ -5456,15 +5440,19 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans,
ins->offset = num_bytes; ins->offset = num_bytes;
if (offset < search_start) if (offset < search_start)
btrfs_add_free_space(block_group, offset, btrfs_add_free_space(used_block_group, offset,
search_start - offset); search_start - offset);
BUG_ON(offset > search_start); BUG_ON(offset > search_start);
if (used_block_group != block_group)
btrfs_put_block_group(used_block_group);
btrfs_put_block_group(block_group); btrfs_put_block_group(block_group);
break; break;
loop: loop:
failed_cluster_refill = false; failed_cluster_refill = false;
failed_alloc = false; failed_alloc = false;
BUG_ON(index != get_block_group_index(block_group)); BUG_ON(index != get_block_group_index(block_group));
if (used_block_group != block_group)
btrfs_put_block_group(used_block_group);
btrfs_put_block_group(block_group); btrfs_put_block_group(block_group);
} }
up_read(&space_info->groups_sem); up_read(&space_info->groups_sem);
......
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